2010-08-17 3 views
5

Dans un programme C++ multithread, j'ai l'équivalent de cette course dans un thread:vide while ne pas vérifier l'état

while(obj->member) { } // waiting for obj->member to be set to false in another thread 

et dans un autre thread, obj-> membre est défini sur false. Même si elle est définie sur false, la boucle ne se casse pas. Si je change à ceci:

while(obj->member) { Sleep(1) } 

Il fonctionne en tant que membre prévu et des pauses lorsque obj-> est définie sur false dans l'autre thread.
Pourquoi ça marche comme ça?

+5

Veuillez ne pas utiliser l'attente occupée - en savoir plus sur les variables d'état ou les primitives de synchronisation similaires. – Arkadiy

+0

Quoi? Je n'ai aucune idée de ce que ça veut dire. – Hock

+0

Je vais appuyer ce que @Arkadiy a dit (et si vous n'avez aucune idée de ce que cela signifie, vous devez faire beaucoup de lecture avant d'écrire du code multithread). En plus de l'attente occupée (qui est simplement une mauvaise forme), votre code n'est pas non plus thread thread. Compte tenu de la simplicité de ce que vous faites dans ce cas précis, cependant, je ne pense pas que vous rencontrerez un problème sur une architecture CPU commune. – rmeador

Répondre

10

Essayez de devenir membre volatile. Cela le forcera à être récupéré de la mémoire chaque fois qu'il est utilisé, plutôt que d'un registre de CPU (ce qui pourrait être optimisé par le compilateur).

6

Obj doit être déclaré volatile. Cela indique au compilateur que sa valeur peut changer par un autre thread.

Lorsque vous ajoutez le Sleep, le compilateur sait que d'autres threads sont en cours d'exécution et suppose qu'il peut être modifié.

+0

Le compilateur sait que du mot-clé 'volatile', pas la présence de' Sleep'. –

+0

@CareyGregory Sauf, apparemment, le compilateur était capable de le savoir à partir de seulement 'Sleep()', basé sur l'OP. –

+0

Peut-être, mais je suis sceptique. Il n'y a aucune raison que la présence de 'Sleep' indique qu'il y a d'autres threads dans le processus. L'ajout de 'Sleep' ou d'une autre opération de rendement dans une boucle serrée est très commun même dans les applications à un seul thread. Je pense qu'il est plus probable que la boucle inflexible d'OP ait affamé suffisamment l'autre thread pour qu'il ne soit jamais capable de mettre le drapeau. –

4

Le fait que cela fonctionne est principalement accidentel. Si vous voulez faire de telles choses de manière fiable, vous avez juste besoin d'utiliser un mécanisme IPC fourni par le système d'exploitation, éventuellement avec Boost Interprocess (par exemple, mutex ou sémaphore) pour donner un frontal plus portable.

volatile, bien que souvent présenté comme une solution à de tels problèmes, n'est ni nécessaire ni suffisant. Cela peut nuire à la performance (beaucoup) mais n'est pas suffisant pour que le thread fonctionne correctement. Si vous programmez pour .NET, Microsoft a défini sa version de volatile pour fournir (au moins un certain degré de) sécurité de thread. Sinon (en vrai C ou C++), il est peu utile et peut causer des dommages considérables.

Je devrais également mentionner que c'est long façons de la première fois que cette erreur a été faite. Il ya longtemps, pas moins d'une autorité que Andre Alexandrescu a écrit un article assez substantiel dans Doctor Dobbs sur l'utilisation de volatile pour le filetage. Il s'est ensuite rendu compte qu'il avait tort, mais (dans une large mesure) les dégâts ont été faits - d'autant plus que volatile est tellement proche de faire les bonnes choses qu'il est extrêmement facile de se tromper pour être correct/utile dans le filetage.

0

Votre bibliothèque de threads effectue peut-être une copie de l'objet lors de la création du thread et le membre n'est pas réellement partagé entre les threads à moins qu'il ne soit déclaré statique.

+0

Non, c'est un pointeur sur un objet. – Hock

2

Une meilleure façon de le faire serait d'utiliser un événement à la place du booléen. Avec un événement, vous pouvez l'utiliser sans utiliser de CPU, et vous n'aurez pas besoin de dormir ou d'utiliser Volitile.