Qu’est-ce que la contention de fil?

Quelqu’un peut-il s’il vous plaît expliquer simplement quelle est la contention de fil?

Je l’ai googlé, mais je n’arrive pas à trouver une explication simple.

Essentiellement, le conflit de thread est une condition où un thread attend un verrou / object actuellement détenu par un autre thread. Par conséquent, ce thread en attente ne peut pas utiliser cet object tant que l’autre thread n’a pas déverrouillé cet object particulier.

Plusieurs réponses semblent se concentrer sur la contention des verrous, mais les verrous ne sont pas les seules ressources sur lesquelles un conflit peut se produire. La dispute se produit simplement lorsque deux threads tentent d’accéder à la même ressource ou à des ressources associées de telle manière qu’au moins un des threads en conflit s’exécute plus lentement que si les autres threads ne s’exécutaient pas.

L’exemple le plus évident de conflit est sur un verrou. Si le thread A a un verrou et que le thread B souhaite acquérir ce même verrou, le thread B devra attendre que le thread A libère le verrou.

Maintenant, ceci est spécifique à la plate-forme, mais le thread peut connaître des ralentissements même s’il n’a jamais à attendre que l’autre thread libère le verrou! En effet, un verrou protège certains types de données et les données elles-mêmes sont souvent contestées.

Par exemple, considérons un thread qui acquiert un verrou, modifie un object, puis libère le verrou et effectue d’autres opérations. Si deux threads font cela, même s’ils ne se battent jamais pour le verrou, les threads peuvent s’exécuter beaucoup plus lentement que si un seul thread était en cours d’exécution.

Pourquoi? Supposons que chaque thread s’exécute sur son propre cœur sur un processeur x86 moderne et que les cœurs ne partagent pas de cache L2. Avec un seul thread, l’object peut restr dans le cache L2 la plupart du temps. Avec les deux threads en cours d’exécution, chaque fois qu’un thread modifie l’object, l’autre thread trouvera que les données ne se trouvent pas dans son cache L2 car l’autre CPU a invalidé la ligne de cache. Sur un Pentium D, par exemple, le code s’exécute à la vitesse du FSB, ce qui est bien inférieur à la vitesse du cache L2.

Étant donné que des conflits peuvent survenir même si le verrou n’est pas contesté, des conflits peuvent également survenir en l’absence de verrou. Par exemple, supposons que votre CPU prenne en charge un incrément atomique d’une variable 32 bits. Si un thread continue à incrémenter et à décrémenter une variable, la variable sera chaude dans le cache la plupart du temps. Si deux threads le font, leurs caches se disputeront la propriété de la mémoire contenant cette variable, et de nombreux access seront plus lents lorsque le protocole de cohérence de la mémoire cache fonctionnera pour sécuriser chaque propriétaire de la ligne de cache.

Ironiquement, les verrous réduisent généralement les conflits. Pourquoi? Parce que sans un verrou, deux threads peuvent opérer sur le même object ou la même collection et provoquer beaucoup de conflits (par exemple, il existe des files d’attente sans verrou). Les verrous auront tendance à programmer les threads en conflit, ce qui permettra aux threads non rivaux de s’exécuter à la place. Si le thread A détient un verrou et que le thread B souhaite utiliser ce même verrou, l’implémentation peut exécuter le thread C à la place. Si le thread C n’a pas besoin de ce verrou, alors les conflits futurs entre les threads A et B peuvent être évités pendant un certain temps. (Bien sûr, cela suppose que d’autres threads pourraient être exécutés. Cela n’aidera pas si la seule façon dont le système dans son ensemble peut faire des progrès utiles est d’exécuter des threads rivaux.)

De là :

Un conflit se produit lorsqu’un thread attend une ressource qui n’est pas facilement disponible. cela ralentit l’exécution de votre code, mais peut disparaître avec le temps.

Un blocage se produit lorsqu’un thread attend une ressource verrouillée par un second thread et que le second thread attend une ressource verrouillée par le premier thread. Plus de deux threads peuvent être impliqués dans un blocage. Une impasse ne se résout jamais elle-même. Cela provoque souvent l’arrêt complet de l’application ou de la partie qui rencontre le blocage.

Vous avez 2 threads. Thread A et Thread B, vous avez aussi l’object C.

Un accède actuellement à l’object C et a placé un verrou sur cet object. B doit accéder à l’object C, mais ne peut pas le faire jusqu’à ce que A libère le verrou sur l’object C.

Je pense qu’il devrait y avoir des éclaircissements de la part du PO sur le contexte de la question – je peux penser à 2 réponses (même si je suis sûr qu’il y a des ajouts à cette liste):

  1. Si vous faites référence au “concept” général de contention de thread et à la manière dont il peut se présenter dans une application, je m’en remets à la réponse détaillée de @ DavidSchwartz ci-dessus.

  2. Il y a aussi les «verrous et threads CLR .NET: nombre total de compteurs de performance». D’après la description de PerfMon pour ce compteur, il est défini comme suit:

    Ce compteur affiche le nombre total de fois que les threads du CLR ont tenté d’acquérir un verrou géré sans succès. Les verrous gérés peuvent être acquis de plusieurs manières. par l’instruction “lock” en C # ou en appelant System.Monitor.Enter ou en utilisant l’atsortingbut personnalisé MethodImplOptions.Synchronized.

… et j’en suis sûr pour les autres systèmes d’exploitation et les frameworks d’application.

Un autre mot pourrait être la concurrence. C’est simplement l’idée de deux ou plusieurs threads essayant d’utiliser la même ressource.

À mes yeux, il y a une concurrence entre deux threads ou plus sur une ressource partagée. La ressource peut être une serrure, un comptoir, etc. La compétition signifie “qui l’obtient en premier”. Plus il y a de fils, plus il y a de conflits. L’access plus fréquent à une ressource est le plus controversé.

La contention de thread est également affectée par les opérations d’E / S. Exemple lorsqu’un thread en attente de lecture du fichier peut être considéré comme un conflit. Utilisez les ports d’achèvement d’E / S comme solution.

Un conflit de locking se produit lorsqu’un thread tente d’acquérir le verrou sur un object déjà acquis par un autre thread *. Tant que l’object n’est pas libéré, le thread est bloqué (en d’autres termes, il est en attente). Dans certains cas, cela peut conduire à une exécution en série qui affecte négativement l’application.

de la documentation de dotTrace

Imaginez le scénario suivant. Vous vous préparez pour l’examen final de demain et vous sentez un peu faim. Donc, vous donnez dix dollars à votre jeune frère et lui demandez d’acheter une pizza pour vous. Dans ce cas, vous êtes le fil conducteur et votre frère est un fil enfant. Une fois votre commande passée, vous et votre frère faites leur travail en même temps (c.-à-d. Étudier et acheter une pizza). Maintenant, nous avons deux cas à considérer. Tout d’abord, votre frère ramène votre pizza et se termine pendant que vous étudiez. Dans ce cas, vous pouvez arrêter d’étudier et profiter de la pizza. Deuxièmement, vous terminez votre étude tôt et dormez (c.-à-d. Votre travail assigné pour aujourd’hui – étudiez pour l’examen final de demain – est terminé) avant que la pizza ne soit disponible. Bien sûr, vous ne pouvez pas dormir; sinon, vous n’aurez pas la chance de manger la pizza. Ce que vous allez faire, c’est attendre que votre frère ramène la pizza.

Comme dans l’exemple, les deux cas donnent un sens à la rivalité.