Certaines implémentations sont-elles meilleures que d'autres pour des applications spécifiques? Y a-t-il quelque chose à gagner en déployant le vôtre?
Consultez la description de l'instruction machine Test-and-set sur Wikipedia, qui fait référence à la façon dont les opérations atomiques sont réalisées au niveau de la machine. Je peux imaginer que la plupart des implémentations de mutex au niveau du langage reposent sur un support au niveau de la machine tel que Test-and-set.
En vous appuyant sur la suggestion test-and-set
D'Adamski, vous devriez également examiner le concept de "mutex rapides de l'espace utilisateur" ou futexes .
Les Futex ont la propriété souhaitable de ne pas nécessiter d'appel système du noyau dans les cas courants de verrouillage ou de déverrouillage d'un mutex non contraint . Dans ces cas, le code en mode utilisateur utilise avec succès une opération atomique comparer et échanger (CAS) pour verrouiller ou déverrouiller le mutex.
Si CAS échoue, le mutex est contesté et un appel système du noyau - sys_futex
Sous Linux - doit être utilisé soit pour attendre le mutex (dans le cas de verrouillage) ou pour réveiller d'autres threads (dans le cas de déverrouillage ).
Si vous envisagez sérieusement de l'implémenter vous-même, assurez-vous de lire également papier d'Ulrich Drepper.
Un mutex s'exécute de préférence dans le noyau du système d'exploitation tout en gardant la quantité de code qui l'entoure aussi courte que possible, de sorte qu'il peut éviter d'être coupé lors du basculement de tâche vers un autre processus. L'implémentation exacte est donc un peu secrète. Ce n'est pas complexe cependant. C'est fondamentalement un objet qui a un champ booléen, qu'il obtient et définit.
Autour de la logique mutex de base, il y a des wrappers pour l'envelopper dans un objet. Puis d'autres objets wrapper pour le rendre disponible en dehors du noyau. Et puis un autre wrapper pour le rendre disponible en .NET. Et puis plusieurs programmeurs écriront leur propre code wrapper autour de tout cela pour leurs propres besoins logiques. Les emballages autour des emballages en font vraiment un territoire trouble.
Maintenant, avec ces connaissances de base sur les composants internes des mutex, tout ce que j'espère, c'est que vous allez utiliser une implémentation qui repose sur le noyau et le matériel en dessous. Ce seraient les plus fiables. (Si le matériel les prend en charge.) Si le mutex que vous utilisez ne fonctionne pas à ce niveau noyau/matériel, il peut toujours être fiable, mais je vous conseillerais de ne pas l'utiliser, sauf s'il n'y a pas d'alternative.
Pour autant que je sache, Windows, Linux et .NET utiliseront tous des mutex au niveau du noyau/matériel.
La page Wikipedia à laquelle j'ai lié explique plus sur la logique interne et les implémentations possibles. De préférence, un mutex est contrôlé par le matériel, ce qui rend l'ensemble/obtention du mutex un étape indivisible . (Juste pour vous assurer que le système ne change pas de tâche entre les deux.)
Interlocked.CompareExchange
est suffisant pour implémenter des verrous tournants. C'est assez difficile à faire correctement. Voir pour le blog de Joe Duffy pour un exemple des subtilités impliquées.
Un peu d'assemblage pour démontrer le verrouillage atomique:
; BL is the mutex id
; shared_val, a memory address
CMP [shared_val],BL ; Perhaps it is locked to us anyway
JZ .OutLoop2
.Loop1:
CMP [shared_val],0xFF ; Free
JZ .OutLoop1 ; Yes
pause ; equal to rep nop.
JMP .Loop1 ; Else, retry
.OutLoop1:
; Lock is free, grab it
MOV AL,0xFF
LOCK CMPXCHG [shared_val],BL
JNZ .Loop1 ; Write failed
.OutLoop2: ; Lock Acquired