Problème avec un problème de synchronisation pThread

Je suis confronté à un problème de synchronisation avec pthread. threadWaitFunction1, est une fonction d’attente de thread. J’attends la ligne no. 247 flag = 1 à exécuter uniquement après 243-246. Mais je trouve étrange que parfois, il saute directement à 247 avant que 243-246 ne soit terminé.

Aidez-moi, s’il vous plaît.

Merci d’avance.

 236 struct timespec timeToWait; 237 static void* threadWaitFunction1(void *timeToWaitPtr) 238 { 239 cout << "Setting flag =0 inside threadWaitFunction1\n"; 240 241 cout << "Inside threadWaitFunction\n"; 242 struct timespec *ptr = (struct timespec*) timeToWaitPtr; 243 pthread_mutex_lock(&timerMutex); flag = 0; 244 pthread_cond_timedwait(&timerCond, &timerMutex, ptr); flag=1; 245 pthread_mutex_unlock(&timerMutex); 246 cout << "Setting flag =1 inside threadWaitFunction1\n"; 247 248 249 } 

Le fil qui crée et appelle le fil ci-dessus est:

 263 static void timer_trackStartTime () 264 { 265 struct timeval now; 266 pthread_t thread; 267 268 printf("Inside trackStartTime: flag = %d\n",flag); 269 270 /* Setting timer expiration */ 271 timeToWait.tv_sec = lt_leak_start_sec;; // First expiry after 1 sec 272 timeToWait.tv_nsec = lt_leak_start_nsec; 273 pthread_create(&thread, NULL, threadWaitFunction1, &timeToWait); 274 pthread_join(thread, NULL); 275 //pthread_kill(thread, SIGKILL); // Destroying the thread to ensure no leaks 276 . . 283 } 

Si je protège la fonction entière en utilisant pthread_mutex_lock, mais le même problème persiste. Comment assurer une exécution ordonnée? Quelqu’un peut-il aider?

EDIT: now.tv_sec et now.tv_nsec supprimés du code. * EDIT: Changé les drapeaux à l’intérieur du mutex (ne fonctionne toujours pas) *

Donc, ce n’est pas vraiment un ordre d’exécution (ce qui est probablement le cas), mais un timing qui vous rend malheureux. Et sous “il saute directement à 247 avant que 243-246 ait terminé”, vous voulez dire “je l’ai observé en train d’exécuter 247 avant que le délai d’attente de 244 ne soit écoulé”. Droite?

Ensuite, je soupçonne qu’il s’agit d’un problème de réveil parasite : un thread peut se réveiller alors même qu’aucun autre thread n’a signalé la variable de condition. La spécification de pthread_cond_timedwait() indique que “des fonctions parasites provenant des fonctions pthread_cond_timedwait () ou pthread_cond_wait () peuvent se produire”.

Habituellement, une variable de condition est associée à un certain événement dans l’application, et un thread en attente d’une variable de condition attend en fait un signal d’un autre thread indiquant que l’événement en question s’est produit. Si vous n’avez pas d’événement et que vous voulez juste attendre un certain temps, d’autres méthodes, telles que usleep() ou les timers , sont plus appropriées, sauf si vous avez également besoin d’un point d’annulation Pthread.

AJOUTÉ: Puisque vous semblez satisfait de usleep() et que vous ne demandez qu’à savoir pourquoi pthread_cond_timedwait() n’a pas répondu à vos attentes, j’ai décidé de ne pas poster le code. Si vous en avez besoin, vous pouvez utiliser la réponse de @Hasturkun.


ADDED-2: La sortie dans les commentaires ci-dessous (obtenue après l’application de la solution de Hasturkun) suggère que le thread en attente ne quitte pas la boucle, ce qui signifie probablement que pthread_cond_timedwait() renvoie quelque chose de différent d’ETIMEDOUT. Avez-vous vu le commentaire de @nos dans votre message (j’ai fixé la quantité de nanosecs à soustraire):

Assurez-vous que (now.tv_usec * 1000) + lt_leak_start_nsec; ne déborde pas. Vous ne pouvez définir tv_nsec que sur 999999999, si l’expression est plus grande que cela, vous devez soustraire 1 000 000 de tv_nsec et incrémenter tv_sec de 1. Si votre timeToWaitPtr contient un tv_nsec non valide (supérieur à 999999999), pthread_cond_timed échouera (vous devrez vérifier) sa valeur de retour aussi.) – nos 28 avr à 19:04

Dans ce cas, pthread_cond_timedwait() retournera EINVAL plusieurs resockets et ne sortira jamais de la boucle. Il est préférable d’ajuster le délai d’attente avant d’entrer dans la boucle d’attente, bien que cela puisse également être fait en réponse à EINVAL .


ADDED-3: Maintenant que vous avez modifié le code de votre question pour que le délai d’expiration expire sans augmenter l’heure, il existe un autre problème. Comme indiqué dans la spécification , le délai d’attente pour pthread_cond_timedwait() est un temps absolu et non pas relatif; ainsi, lorsque vous passez quelque chose comme 3 secondes comme délai d’attente, il est interprété comme “3 secondes depuis le sharepoint référence pour l’heure système”. Ce moment est presque certainement passé depuis un moment, et pthread_cond_timedwait() revient immédiatement.
Je vous recommande de lire attentivement la spécification (y compris la justification) pour mieux comprendre comment cette fonction est censée être utilisée.

Paul E. McKenney a écrit un livre intitulé “La programmation parallèle est-elle difficile et, dans l’affirmative, que pouvez-vous y faire?” , qui contient de très bonnes informations (et quelques belles images) sur les barrières de mémoire.

Pour revenir à votre question, le flag n’est protégé par rien. Bien que vous puissiez penser que pthread_mutex_lock() et pthread_mutex_unlock fournissent des garanties fortes en matière de commande et de visibilité, les seules garanties qu’il fournit sont pour les access à l’intérieur de la région critique et pour le mutex lui-même.

De plus, sur certaines architectures, pthread_mutex_lock() utilise une barrière d’acquisition et pthread_mutex_unlock() utilise une barrière de libération, ce qui signifie que les access avant et après la région protégée par mutex peuvent déborder dans la région protégée par mutex. De fortes garanties de commande sont fournies entre une unité centrale libérant un mutex et une autre unité centrale acquérant le même mutex, mais à peu près tout le rest n’a pas besoin (et n’a peut-être pas lieu de telles garanties).

Modifier:

Apparemment, je me suis trompé en ce qui concerne les pthreads, elles semblent exiger des barrières de mémoire complètes (si vous interprétez la synchronisation de la mémoire par rapport aux autres threads comme exigeant cela). Pour en savoir plus à ce sujet, ainsi que des informations sur les garanties fournies dans les implémentations réelles, dans la section Réorganisation des contraintes pour les verrous de style Pthread de Hans Boehm.

Je m’interroge également sur NPTL sous IA64 1 , 2 .

Selon Alexey Kukanov, le problème est probablement un réveil fallacieux. votre code peut être corrigé en boucle jusqu’à l’expiration du délai. Notez que j’ai également déplacé le paramètre de drapeau pour qu’il soit sous le mutex.

 static void* threadWaitFunction1(void *timeToWaitPtr) { struct timespec *ptr = (struct timespec*) timeToWaitPtr; int ret; pthread_mutex_lock(&timerMutex); cout << "Setting flag =0 inside threadWaitFunction1\n"; flag=0; cout << "Inside threadWaitFunction\n"; while (pthread_cond_timedwait(&timerCond, &timerMutex, ptr) != ETIMEDOUT) ; cout << "Setting flag =1 inside threadWaitFunction1\n"; flag=1; pthread_mutex_unlock(&timerMutex); } 

Pour être du côté sûr, vous devriez vérifier le drapeau sous le même mutex pour établir la commande

Cela peut être dû au fait que le compilateur a optimisé les choses et place votre affectation à votre drapeau avant le thread mutex. Si vous voulez garantir un ordre d’exécution (ce qui n’est normalement pas garanti, à la seule condition que le comportement visible de votre programme ne change pas à cause d’optimisations), vous utilisez une barrière de mémoire pour vous assurer que les instructions que vous voulez être exécutés dans l’ordre dans lequel vous les écrivez, ne sont exécutés que dans cet ordre.

Voici un article très intéressant, bien que plutôt technique et long, sur le fonctionnement des barrières de mémoire, sur ce qu’elles font et ne font pas. C’est écrit pour Linux, mais les principes de base restnt les mêmes.

MODIFIER:

Le verrou est une barrière de mémoire implicite, par le lien que j’ai donné précédemment, donc aucune barrière de mémoire n’est nécessaire.

Juste pour l’information de tous:

Ce que je ne pouvais pas réaliser en utilisant pthread_cond_timedwait(&timerCond, &timerMutex, ptr); J’ai réussi à utiliser usleep( ) , usleep prend la structure de timespec où nous pouvons spécifier la période d’attente en secondes et nanosecondes, et mon objective est résolu.

Alors, qu’est-ce que pthread_cond_timedwait(&timerCond, &timerMutex, ptr); avoir un sens pour ?? Je suis surpris, car cette API est censée faire attendre le thread appelant pour que cette condition soit satisfaite, mais il semble que le processeur passe à l’instruction suivante en tant que mesure d’optimisation et n’attende pas que la condition soit satisfaite.

Mais toujours mon problème rest le même, quant à pourquoi, pthread_cond_timedwait(&timerCond, &timerMutex, ptr); ne devrait pas faire attendre le fil d’appel?

Il semble que j’ai perdu un jour derrière cette API: pthread_cond_timedwait( )