Golang est un langage de programmation efficace et concis, et ses bonnes performances et sa facilité d'utilisation se reflètent dans l'application de la programmation simultanée. En programmation simultanée, Mutex est un mécanisme de synchronisation très courant. Il peut garantir un accès mutuellement exclusif aux ressources partagées dans un environnement multithread tout en évitant les conditions de concurrence (c'est-à-dire des résultats imprévisibles lorsque plusieurs threads accèdent aux ressources partagées en même temps). Cet article présentera le principe de mise en œuvre sous-jacent de Mutex.
1. Définition du Mutex
Dans Golang, Mutex est un mécanisme de synchronisation utilisé pour protéger l'accès mutuellement exclusif aux ressources partagées. Il contient deux méthodes : Lock() et Unlock(), qui sont utilisées respectivement pour verrouiller et déverrouiller Mutex. Lorsqu'un thread acquiert le verrou du Mutex, les autres threads seront bloqués jusqu'à ce que le verrou soit libéré.
2. L'implémentation sous-jacente de Mutex
Dans Golang, l'implémentation sous-jacente de Mutex repose principalement sur une structure appelée sync.Mutex. Mutex est implémenté via l'opération CAS (Compare-And-Swap), qui repose sur le fonctionnement atomique du matériel sous-jacent.
La structure Mutex est définie comme suit :
type Mutex struct { state int32 sema *uint32 // 信号量 } const ( mutexLocked = 1 << iota // mutex is locked ) func (m *Mutex) Lock() { // Fast path: 这里如果加锁成功,就直接返回 if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { return } // Slow path : 防止出现busy spinning,将当前协程加入等待队列,然后调用runtime.semacquire继续sleep。 sema := m.sema if sema == nil { sema = new(uint32) m.sema = sema } runtime_Semacquire(sema) } func (m *Mutex) Unlock() { // Fast path: 这里如果释放锁成功,就直接返回 if atomic.CompareAndSwapInt32(&m.state, mutexLocked, 0) { return } // Slow path: 如果锁还被持有,则调用sync.runtime_Semrelease继续唤醒协程。 sema := m.sema if sema == nil { panic("sync: unlock of unlocked mutex") } runtime_Semrelease(sema, false, 1) }
La structure Mutex contient deux champs, un état et un sémaphore sema. Parmi eux, l'état indique l'état du verrou, mutexLocked indique qu'il est verrouillé et d'autres valeurs indiquent qu'il n'est pas verrouillé. sema est utilisé pour coordonner les goroutines en attente de verrous.
Dans la méthode Mutex.Lock(), si le Mutex actuel n'est pas verrouillé, utilisez l'opération atomique CompareAndSwapInt32 pour changer l'état de 0 à mutexLocked, et revenez directement après le succès sinon, laissez le courant ; goroutine rejoint la file d'attente et appelle la méthode runtime.semacquire() pour la réveiller. Dans la méthode Mutex.Unlock(), si le Mutex actuel est verrouillé, utilisez l'opération atomique CompareAndSwapInt32 pour changer l'état de mutexLocked à 0, et revenez directement après le succès, sinon une exception est levée, indiquant que le Mutex actuel n'est pas ; fermé.
Il existe des chemins rapides et des chemins lents dans les méthodes Mutex.Lock() et Mutex.Unlock(). Le chemin rapide signifie que lorsque l'état du verrou n'est pas occupé, le verrou peut être rapidement obtenu ou le verrou peut être rapidement libéré via CAS. Le chemin lent signifie que lorsque l'état de verrouillage est occupé, la goroutine actuelle doit être ajoutée à la file d'attente et la méthode sema.acquire() est appelée pour la mettre en veille ou réveiller les autres goroutines dans la file d'attente.
3. Malentendus dans l'utilisation de Mutex
Dans Golang, Mutex est un mécanisme de synchronisation très couramment utilisé, mais lors de son utilisation, il existe certains malentendus courants que nous devons éviter.
Si une goroutine essaie de libérer un Mutex, elle ne le fait pas maintenez , le programme va semer la panique. Les verrous Mutex doivent garantir un accès mutuellement exclusif aux ressources partagées. Si un goroutine peut libérer le verrou, son exclusivité mutuelle ne peut pas être garantie.
Le verrouillage Mutex est un type de pointeur Si vous devez copier la variable de verrouillage, vous devez utiliser la copie de pointeur. . Sinon, cela entraînera deux instances Mutex non liées partageant le même état, ce qui peut entraîner un comportement inattendu.
En programmation simultanée, la concurrence libre pour les verrous fait référence à avant qu'une goroutine ne libère le verrou. continuera d'essayer d'acquérir le verrou au lieu d'attendre dans la file d'attente. Cela entraînera un gaspillage des ressources CPU, et cette situation doit être évitée autant que possible.
En bref, les verrous sont un outil puissant pour protéger les ressources partagées et jouent un rôle très important dans la programmation concurrente. À travers cet article, nous comprenons le principe sous-jacent de mise en œuvre de Mutex, ainsi que certains malentendus auxquels il faut prêter attention lors de l'utilisation de Mutex. Dans le développement réel, nous devons exploiter pleinement les avantages de Mutex pour éviter divers problèmes pouvant survenir lors de la programmation simultanée.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!