Certes, beaucoup ont entendu, mais quelqu'un s'est rencontré dans la pratique, tels que des blocages et des conditions de course. Ces concepts sont classés comme des erreurs dans l'utilisation de la concurrence. Si je vous pose une question sur ce qu'est un blocage, vous êtes très susceptible de commencer à dessiner une image de blocage classique ou sa représentation en pseudo-code sans aucun doute. Quelque chose comme ça:

Nous obtenons ces informations à l'institut, qui peuvent être trouvées dans des livres et des articles sur Internet. Une telle impasse utilisant, par exemple, deux mutex, dans toute sa splendeur, peut être trouvée dans le code. Mais dans la plupart des cas, tout n'est pas aussi simple, et tout le monde ne peut pas voir le modèle d'erreur classique dans le code s'il n'est pas présenté sous la forme habituelle.

Considérons une classe dans laquelle nous nous intéressons aux méthodes StartUpdate, CheckAndUpdate et Stop, C ++ est utilisé, le code est aussi simple que possible:
std::recursive_mutex m_mutex; Future m_future; void Stop() { std::unique_lock scoped_lock(m_mutex); m_future.Wait();
À quoi devez-vous faire attention dans le code présenté:
- mutex récursif est utilisé. La capture répétée d'un mutex récursif ne mène à l'attente que si cette capture se produit dans le même thread. Dans ce cas, le nombre d'exemptions mutex devrait correspondre au nombre de captures. Si nous essayons de capturer un mutex récursif qui est déjà capturé dans un autre thread, le thread passe en mode veille.
- La fonction Future :: Schedule démarre (en n millisecondes) dans un thread séparé auquel le rappel lui est passé
Maintenant, nous analysons toutes les informations reçues et composons une image:

Compte tenu des deux faits présentés ci-dessus, il n'est pas difficile de conclure qu'une tentative de capture d'un mutex récursif dans l'une des fonctions entraînera l'attente de la libération du mutex s'il a déjà été capturé dans une autre fonction, car le rappel CheckAndUpdate est toujours exécuté dans un thread séparé.
À première vue, il n'y a rien de suspect concernant l'impasse. Mais pour être plus proche, tout se résume à notre image classique. Lorsque l'objet fonctionnel commence à s'exécuter, nous capturons implicitement la ressource m_future, le rappel directement
associé à m_future:
La séquence d'actions menant à l'impasse est la suivante:- Il est prévu d'exécuter CheckAndUpdate, mais le rappel ne démarre pas immédiatement, après n millisecondes.
- La méthode Stop est appelée, puis elle démarre: nous essayons de capturer le mutex - la ressource est une capturée, nous commençons à attendre que m_future se termine - l'objet n'a pas encore été appelé, nous attendons.
- L'exécution de CheckAndUpdate commence: nous essayons de capturer le mutex - nous ne pouvons pas, la ressource est déjà capturée par un autre thread, nous attendons sa libération.
C'est tout: le thread effectuant l'appel Stop attend la fin de CheckAndUpdate, et l'autre thread, à son tour, ne peut pas continuer à fonctionner jusqu'à ce qu'il attrape le mutex qui a déjà été capturé par le thread mentionné précédemment. C'est une impasse assez classique. La moitié du travail est effectuée - la cause du problème a été découverte.
Maintenant, un peu sur la façon de le réparer.Approche 1La procédure de capture des ressources doit être la même, cela évitera les blocages. Autrement dit, vous devez voir s'il est possible de modifier l'ordre de capture des ressources dans la méthode Stop. Dans la mesure où le cas de blocage n'est pas entièrement évident et qu'il n'y a pas de capture explicite de la ressource m_future dans CheckAndUpdate, nous avons décidé de réfléchir à une autre solution afin d'éviter le retour d'erreur à l'avenir.
Approche 2- Vérifiez si vous pouvez désactiver l'utilisation du mutex dans CheckAndUpdate.
- Puisque nous utilisons le mécanisme de synchronisation, nous restreignons l'accès à certaines ressources. Il vous suffira peut-être de refaire ces ressources en atomique (comme nous l'avons fait), dont l'accès est déjà thread-safe.
- Il s'est avéré que les variables, dont l'accès était limité, peuvent être facilement converties en atomiques, de sorte que le mutex mentionné est supprimé avec succès.
Voici un exemple simple avec un blocage non évident qui se réduit facilement au modèle de cette erreur. Enfin, je souhaite que vous écriviez du code fiable et sûr pour les threads!