signaler et déverrouiller la commande

void WorkHandler::addWork(Work* w){ printf("WorkHandler::insertWork Thread, insertWork locking \n"); lock(); printf("WorkHandler::insertWork Locked, and inserting into queue \n"); m_workQueue.push(w); signal(); unLock(); } 

J’ai suivi un tutoriel et je l’ai eu. Je me demandais s’il était possible de changer l’ordre de singal () et unLock () comme ceci

 void WorkHandler::addWork(Work* w){ printf("WorkHandler::insertWork Thread, insertWork locking \n"); lock(); printf("WorkHandler::insertWork Locked, and inserting into queue \n"); m_workQueue.push(w); unLock(); signal(); } 

Si je ne peux pas faire cela, pourriez-vous s’il vous plaît me donner des détails, pourquoi je ne suis pas autorisé à le faire? Merci d’avance.

Tout d’abord, il n’y a pas de problème de correction ici. L’un ou l’autre ordre fonctionnera. Rappelez-vous que chaque fois que vous utilisez des variables de condition, vous devez boucler un prédicat en attendant:

 pthread_mutex_lock(mutex); while (!predicate) pthread_cond_wait(cvar); pthread_mutex_unlock(mutex); 

En signalant après le délocking, vous n’introduisez aucun problème de correction; le thread est toujours garanti pour se réveiller, et le pire des cas est un autre réveil vient en premier – à quel point il voit le prédicat devient vrai et procède.

Cependant, deux problèmes de performances possibles peuvent survenir.

  • “Dépêche-toi et attends”. En gros, si vous signalez que le verrou est maintenu, l’autre thread doit encore attendre que le mutex soit disponible. Au lieu de réveiller l’autre thread, de nombreuses implémentations de pthreads déplaceront simplement celui-ci dans la file d’attente du mutex, ce qui économisera un cycle inutile de réveil-> attente. Dans certains cas, toutefois, cela n’est pas implémenté ou disponible, ce qui peut entraîner un commutateur de contexte parasite ou un IPI potentiel.
  • Wakeups parasites. Si vous signalez après le délocking, il est possible qu’un autre thread émette un autre réveil. Considérez le scénario suivant:

    1. Le fil A commence à attendre que des éléments soient ajoutés à une queue threadsafe.
    2. Le fil de discussion B insère un élément dans la queue. Après le délocking de la queue, mais avant l’émission du signal, un changement de contexte se produit.
    3. Le fil de discussion C insère un élément dans la queue et émet le signal cvar.
    4. Le fil A se réveille et traite les deux éléments. Il revient ensuite à attendre dans la queue.
    5. Le fil B reprend et signale le cvar.
    6. Le fil A se réveille, puis se rendort immédiatement, car la queue est vide.

    Comme vous pouvez le constater, cela peut entraîner un réveil intempestif, susceptible de vous faire perdre du temps processeur.

Personnellement, je ne pense pas que cela vaille la peine de trop s’inquiéter. Vous ne savez pas souvent si votre implémentation prend en charge le déplacement des serveurs de la variable de condition vers la queue du mutex, ce qui est le seul critère réel que vous pouvez utiliser pour décider lequel utiliser.

Mon intuition serait que, si je devais choisir, signaler après le délocking est légèrement moins susceptible d’introduire une inefficacité, car l’inefficacité nécessite une course à trois threads, plutôt qu’une course à deux threads pour le «dépêche-toi et attends». “condition. Cependant, cela ne vaut vraiment pas la peine de s’inquiéter, à moins que les points de repère ne montrent trop de surcharge de changement de contexte.

La réponse à ta question est oui”. En fait, c’est légèrement préférable (comme vous l’avez probablement deviné) car cela évite le problème de «réveiller et d’attendre» de réveiller un thread pour tester une condition et le bloquer immédiatement sur le mutex qu’il doit acquérir avant de tester le état.

Cette réponse est basée sur l’hypothèse que ces choses sont vraies:

  • lock est un emballage fin pour pthread_mutex_lock .
  • unLock est un wrapper fin pour pthread_mutex_unlock .
  • signal est un wrapper pour pthread_cond_signal .
  • Le mutex que vous verrouillez et déverrouillez est celui que vous donnez à pthread_cond_wait .

Cet article vaut vraiment la peine d’être lu pour répondre à votre question:

Signal avec mutexed ou pas?

En supposant que vous utilisez le même mutex avec la variable conditionnelle pour que le changement de condition soit atomique. Il y a deux cas et vous devriez connaître leur comportement:

  1. attendre le signal (conditionnel var) tout en maintenant le mutex. Le résultat est de laisser le thread rejoindre la queue de la variable conditionnelle, puis de passer en veille.
  2. signalé mais sans mutex. Dans ce cas, le thread ne dormira pas mais se bloquerait dessus. (Une erreur que j’ai commise à ce sujet est que je pensais qu’il dormirait aussi. Dans ce cas, si les signaux du producteur et le changement de contexte se produisent juste avant la publication du mutex, tous les threads se réveillent et savent qu’ils ne peuvent pas verrouiller le mutex. dormir pour toujours, ce qui est faux car ils ne vont pas dormir mais attendent et bloquent).

Les pthreads sont implémentés avec l’ attente-morphing , c’est-à-dire qu’au lieu de réveiller les threads lors de la signalisation, il ne fait que transférer les threads de variable conditionnelle vers la queue mutex attachée. Le signal lors du locking est donc préférable sans impact trop important sur les performances.

La signalisation avant le délocking du mutex peut provoquer un réveil intempestif. Si votre code n’est pas bien conçu pour gérer les modifications de prédicat apscopes par un réveil parasite, vous devez choisir le signal tout en maintenant le verrou.