Lors de l'écriture d'applications multithreads, l'un des problèmes les plus courants est celui des conditions de course.
Les questions que je pose à la communauté sont les suivantes :
Qu'est-ce qu'une race condition ? Comment les détecter ? Comment les gérer ? Enfin, comment les empêcher de se produire ?
Une condition de course se produit lorsque deux ou plusieurs threads peuvent accéder à des données partagées et qu'ils essaient de les modifier en même temps. Comme l'algorithme d'ordonnancement des threads peut permuter entre les threads à tout moment, vous ne connaissez pas l'ordre dans lequel les threads vont tenter d'accéder aux données partagées. Par conséquent, le résultat de la modification des données dépend de l'algorithme d'ordonnancement des threads, c'est-à-dire que les deux threads font la "course" pour accéder aux données ou les modifier.
Les problèmes surviennent souvent lorsqu'un thread fait un "check-then-act" (par exemple, "check" si la valeur est X, puis "act" pour faire quelque chose qui dépend de la valeur X) et un autre thread fait quelque chose à la valeur entre le "check" et le "act". Par exemple
if (x == 5) // The "Check"
{
y = x * 2; // The "Act"
// If another thread changed x in between "if (x == 5)" and "y = x * 2" above,
// y will not be equal to 10.
}
Le fait est que y pourrait être 10, ou n'importe quoi, selon qu'un autre thread a modifié x entre la vérification et l'action. Vous n'avez aucun moyen réel de le savoir.
Afin d'empêcher les situations de course de se produire, vous placez généralement un verrou autour des données partagées pour vous assurer qu'un seul thread peut accéder aux données à la fois. Cela signifie quelque chose comme ceci :
// Obtain lock for x
if (x == 5)
{
y = x * 2; // Now, nothing can change x until the lock is released.
// Therefore y = 10
}
// release lock for x
Une condition de course est une sorte de bogue, qui ne se produit que dans certaines conditions temporelles.
Exemple : Imaginez que vous avez deux threads, A et B.
Dans le fil A :
if( object.a != 0 )
object.avg = total / object.a
Dans le fil B :
object.a = 0
Si le thread A est préempté juste après avoir vérifié que object.a n'est pas null, B fera a = 0
, et quand le thread A gagnera le processeur, il fera une "division par zéro".
Ce bug ne se produit que lorsque le thread A est préempté juste après l'instruction if, c'est très rare, mais cela peut arriver.
Une condition de course est une situation dans la programmation concurrente où deux threads ou processus concurrents se disputent une ressource et où l'état final résultant dépend de celui qui obtient la ressource en premier.