2012-06-09 8 views
3

Je suis relativement nouveau dans la bibliothèque standard C++ et j'utilise des listes de bibliothèques standard pour une implémentation multithread spécifique. J'ai remarqué qu'il pourrait y avoir un truc avec l'utilisation de listes que je n'ai pas vu sur un tutoriel/blog/forum et bien que cela semble évident pour moi ne semble pas être considéré par quiconque. Alors peut-être que je suis trop nouveau et peut-être manquer quelque chose alors j'espère que quelqu'un de plus intelligent que moi peut peut-être valider ce que j'essaie d'accomplir ou m'expliquer ce que je fais de mal. Par conséquent, nous savons qu'en général, les conteneurs de bibliothèque standard ne sont pas thread-safe - mais cela semble plus qu'une règle. Avec les listes, il semble qu'il existe un niveau de tolérance pour la sécurité des threads. Laissez-moi vous expliquer, nous savons que les listes ne sont pas invalidées si nous ajoutons/supprimons de la liste. Le seul itérateur qui obtient invalidée est l'élément supprimé - que vous pouvez fixer avec la ligne de code suivante:Listes et environnements multithread

it = myList.erase(it) 

Alors Maintenant, supposons que nous avons deux fils et les appeler fil 1 et 2. fil

La responsabilité de Thread 1 est d'ajouter à la liste. Il le traite comme une file d'attente, il utilise donc l'appel de fonction std :: list :: push_back().

La responsabilité de Thread 2 est de traiter les données stockées dans la liste comme une file d'attente et ensuite, après le traitement, de supprimer des éléments de la liste.

Il est garanti que Thread 2 ne supprimera pas les éléments de la liste qui viennent d'être ajoutés lors de son traitement et Thread 1 garantit qu'il mettra en attente les données nécessaires bien avant le traitement de Thread 2. Cependant, gardez à l'esprit que des éléments peuvent être ajoutés pendant le traitement de Thread 2.

Il semble donc que ce soit une utilisation raisonnable des listes dans cet environnement multithread sans l'utilisation de verrous pour la protection des données. La raison pour laquelle je dis son raisonnable est parce que, pour l'essentiel, la discussion 2 sera uniquement les données jusqu'à présent de telle sorte qu'il peut retreive l'itérateur fin du courant indiqué par le pseudo-code suivant:

Thread 2 { 
    iter = myList.begin(); 
    lock(); 
    iterEnd = myList.end(); // lock data temporarily in order to get the current 
          // last element in the list 
    unlock(); 
    // perform necessary processing 
    while (iter != iterEnd) { 
     // process data 
     // ... 
     // remove element 
     iter = myList.erase(iter); 
    } 
} 

Discussion 2 utilise un verrou pour un très peu de temps juste pour savoir où arrêter le traitement, mais la plupart du temps Thread 1 et Thread 2 ne nécessitent aucun autre verrouillage. En outre, Thread 2 peut éventuellement éviter le verrouillage si son étendue pour connaître le dernier élément en cours est flexible.

Quelqu'un voit quelque chose de mal avec ma suggestion?

Merci!

Répondre

5

Votre programme est racé. A titre d'exemple d'une course de données évidente: std::list est plus que juste une collection de nœuds doublement liés. Il a également, par exemple, un membre de données qui stocke le nombre de nœuds dans la liste (il ne doit pas être un seul membre de données, mais il doit stocker le compte quelque part).

Vos deux threads modifieront simultanément ce membre de données. Parce qu'il n'y a pas de synchronisation de ces modifications, votre programme est racé.

Les instances des conteneurs de la bibliothèque standard ne peuvent pas être mutées à partir de plusieurs threads simultanément sans synchronisation externe.

+0

De plus, les opérations d'insertion/suppression de liste à double liaison ne sont pas atomiques et peuvent corrompre le nœud étant simultané. Cela se produira si la longueur de la liste est proche de 1. – Basilevs