Alors qu'un mutex peut être utilisé pour résoudre les autres problèmes, la principale raison d'exister est de fournir de l'exclusion mutuelle et ainsi de résoudre ce qui est connu comme une condition de course. Lorsque deux (ou plus) de threads ou processus tente d'accéder à la même variable simultanément, nous avons le potentiel pour une condition de course. Considérons le code suivant
//somewhere long ago, we have i declared as int
void my_concurrently_called_function()
{
i++;
}
Le fonctionnement interne de cette fonction à l'air si simple. C'est une seule instruction. Cependant, une pseudo-langage d'assemblage équivalent pourrait être:
load i from memory into a register
add 1 to i
store i back into memory
Parce que l'équivalent de langue de l'assembly instructions sont nécessaires pour effectuer l'opération d'incrémentation sur i, on dit que l'incrémentation de i est un non-atmoic opération. Une opération atomique est celui qui peut être effectuée sur le matériel avec une garantie de ne pas être interrompu une fois l'instruction, l'exécution a commencé. L'incrémentation de i est constitué d'une chaîne de 3 atomic instructions. Dans un tel système où plusieurs threads sont l'appel de la fonction, les problèmes surgissent quand un thread lit ou écrit au mauvais moment. Imaginons que nous ayons deux threads s'exécutant simultaneoulsy et on appelle la fonction immédiatement après l'autre. Disons aussi que nous avons, je initialisé à 0. Supposons également que nous avons beaucoup de registres et que les deux fils sont complètement à l'aide de différents registres, donc il n'y aura pas de collisions. Le calendrier de ces événements peuvent être:
thread 1 load 0 into register from memory corresponding to i //register is currently 0
thread 1 add 1 to a register //register is now 1, but not memory is 0
thread 2 load 0 into register from memory corresponding to i
thread 2 add 1 to a register //register is now 1, but not memory is 0
thread 1 write register to memory //memory is now 1
thread 2 write register to memory //memory is now 1
Ce qui s'est passé, c'est que nous avons deux fils incrémentation je parallèlement, notre fonction est appelée deux fois, mais le résultat est en contradiction avec ce fait. Il ressemble à la fonction a été appelée seulement une fois. C'est parce que l'atomicité est "cassé" au niveau de la machine, sens les fils peuvent s'interrompre les uns les autres ou de travailler ensemble au mauvais moment.
Nous avons besoin d'un mécanisme pour résoudre ce problème. Nous avons besoin d'imposer certains de commande pour les instructions ci-dessus. Un mécanisme commun est de bloquer tous les fils sauf un. Pthread mutex utilise ce mécanisme.
N'importe quel thread qui doit exécuter quelques lignes de code qui peut mal modifier les valeurs partagées par d'autres threads en même temps (en utilisant le téléphone pour parler à sa femme), doit d'abord acquérir un verrou sur un mutex. De cette façon, n'importe quel thread qui nécessite l'accès aux données partagées doit passer par le mutex lock. C'est alors seulement un thread être en mesure d'exécuter le code. Cette section de code est appelé une section critique.
Une fois que le thread a été exécuté à la section critique, il doit libérer le verrou sur le mutex, de sorte qu'un autre thread puisse acquérir un verrou sur le mutex.
Le concept d'avoir un mutex semble un peu étrange lorsque l'on considère les humains qui cherchent un accès exclusif à de vrais objets physiques, mais lors de la programmation, nous devons être intentionnelle. Threads simultanés et les processus n'ont pas l'sociaux et culturels de l'éducation que nous, alors nous devons les forcer à partager des données bien.
Donc, techniquement parlant, comment un mutex travail? N'est-ce pas souffrir de la même race conditions que nous l'avons mentionné plus tôt? N'est-ce pas pthread_mutex_lock() un peu plus complexe qu'une simple incrémentation d'une variable?
Techniquement parlant, nous avons besoin d'un peu de matériel de soutien pour nous aider. Les concepteurs de matériel nous donner des instructions machine qui ne font plus qu'une chose mais qui sont garantis pour être atomique. Un exemple classique d'une telle instruction est le test-and-set (TAS). Lorsque vous essayez d'acquérir un verrou sur une ressource, on peut utiliser le TAS peut vérifier pour voir si une valeur en mémoire est de 0. Si elle l'est, que serait notre signal que la ressource est en cours d'utilisation et que nous ne faisons rien (ou, plus exactement, nous l'attendons par un mécanisme. Un pthreads mutex va nous mettre dans une file d'attente dans le système d'exploitation et de nous aviser lorsque la ressource est disponible. Dumber systèmes peut nous obliger à faire un serré spin boucle, les tests de la condition). Si la valeur dans la mémoire n'est pas 0, le TAS définit l'emplacement de quelque chose d'autre que 0 sans recourir à d'autres instructions. C'est comme la combinaison de deux instructions de montage en 1 pour nous donner l'atomicité. Ainsi, les essais et la modification de la valeur (en cas de changement est le cas) ne peut pas être interrompu une fois qu'il a commencé. Nous pouvons construire des mutex sur le dessus d'une telle instruction.
Remarque: certaines sections peuvent être similaires à ceux d'une précédente réponse. J'acceptai l'inviter à modifier, il a préféré l'original de la façon dont il a été, donc je vais garder ce que j'avais et qui est infusé avec un peu de son verbiage.