2 votes

Mon programme multithread fonctionne lentement ou semble se bloquer sur une machine à double cœur, merci de m'aider

J'ai un programme avec plusieurs threads, un thread va changer un global lorsqu'il sort de lui-même et l'autre thread va interroger le global de façon répétée. Aucune protection sur les globales. Le programme fonctionne bien sur un uni-processeur. Sur une machine à double cœur, il fonctionne pendant un certain temps puis s'arrête soit sur Sleep(0) soit sur SuspendThread(). Quelqu'un pourrait-il m'aider à résoudre ce problème ?

Le code serait le suivant :

Thread 1:

do something...
while(1)
{
.....
flag_thread1_running=false;
SuspendThread(GetCurrentThread());
continue;

}

Thread 2
flag_thread1_running=true;
ResumeThread(thread1);
.....do some other work here....
while(flag_thread1_running) Sleep(0);
....

14voto

dthorpe Points 23314

Le fait que vous ne voyez aucun problème sur une machine uniprocesseur, mais que vous en voyez sur une machine multiprocesseur est un artefact de la granularité relativement grande du changement de contexte des threads sur une machine uniprocesseur. Un thread s'exécute pendant un temps N (millisecondes, nanosecondes, peu importe) avant que le planificateur de threads ne passe l'exécution à un autre thread. Un grand nombre d'instructions du CPU peuvent être exécutées dans la tranche de temps typique d'un thread. On peut considérer qu'il s'agit d'une assez grande portion de temps processeur exclusif "libre" pendant laquelle vous ne rencontrerez probablement pas de collisions de ressources parce que rien d'autre n'est exécuté sur le processeur.

En revanche, lorsqu'elles sont exécutées sur une machine multiprocessus, les instructions du processeur dans deux threads s'exécutent exactement en même temps. La taille de la portion de temps "libre" est proche de zéro.

Pour reproduire un problème de contention de ressources entre deux threads, il faut que le thread 1 accède à la ressource et que le thread 2 accède à la ressource en même temps ou presque.

Dans la commutation de threads à grande granularité qui a lieu sur une machine uniprocesseur, les chances qu'un changement de thread se produise exactement au bon endroit sont minces, de sorte que le programme peut ne jamais présenter de défaillance dans des conditions normales d'utilisation sur une machine uniprocesseur.

Dans une machine multiprocesseur, les instructions sont exécutées en même temps dans les deux threads, de sorte que les chances que le thread 1 et le thread 2 accèdent à la même ressource au même moment sont beaucoup, beaucoup plus grandes - des milliers de fois plus probables que dans le scénario uniprocesseur.

J'ai vu cela se produire de nombreuses fois : une application qui fonctionnait bien depuis des années sur des machines uniproques commence soudainement à échouer partout lorsqu'elle est exécutée sur une nouvelle machine multiproque. La cause est un bug latent de threading dans le code original qui n'a simplement jamais trouvé la bonne coïncidence de timeslicing pour se reproduire sur les machines uniproc.

Lorsque vous travaillez avec du code multithread, il est absolument impératif de tester le code sur du matériel multiproc. Si vous avez des problèmes de collision de threads dans votre code, ils se manifesteront rapidement sur une machine multiproc.

Comme d'autres l'ont noté, n'utilisez pas SuspendThread() à moins que vous ne soyez un débogueur. Utilisez les mutex ou d'autres objets de synchronisation pour assurer la coordination entre les threads.

3voto

bta Points 22525

Essayez d'utiliser quelque chose comme WaitForSingleObjectEx au lieu de SuspendThread.

2voto

Suma Points 11966

Vous avez un problème de course. Le fil 2 peut exécuter le flag_thread1_running=true ; avant que le thread 1 n'exécute flag_thread1_running=false.

Il est peu probable que cela se produise sur une seule unité centrale, car avec le quantum de programmation habituel de 10-20 ms, vous ne risquez pas de rencontrer le problème. Cela peut également se produire dans ce cas, mais très rarement.

L'utilisation de primitives de synchronisation appropriées est indispensable ici. Au lieu de bool, utilisez event. Au lieu de vérifier le bool dans une boucle, utilisez WaitForSingleObject (ou WaitForMultipleObjects pour des choses plus élaborées plus tard).

Il est possible d'effectuer une synchronisation entre les threads en utilisant des variables simples, mais c'est rarement une bonne idée et il est assez difficile de le faire correctement - cf. Comment puis-je écrire une structure sans verrou ? . Ce n'est certainement pas une bonne idée d'effectuer une planification en utilisant Sleep, Suspend ou Resume.

0voto

D.Shawley Points 30324

Je suppose que vous savez déjà que l'interrogation d'un drapeau global est une "mauvaise idée™", je vais donc sauter ce petit discours. Essayez d'ajouter volatile à la déclaration du drapeau. Cela devrait forcer chaque lecture de celui-ci à lire depuis la mémoire. Sans volatile En effet, l'implémentation pourrait lire le drapeau dans un registre et ne pas le récupérer en mémoire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X