Comment itérer dessus d'un récipient dans un "thread-safe"?
J'ai un conteneur (C++) sur laquelle j'ai besoin d'opérer de deux façons, à partir de threads différents: 1) Ajouter et supprimer des éléments, et 2) l'itération à travers ses membres. Clairement, enlevez l'élément, tandis que l'itération se passe = catastrophe. Le code ressemble à ceci:
class A
{
public:
...
void AddItem(const T& item, int index) { /*Put item into my_stuff at index*/ }
void RemoveItem(const T& item) { /*Take item out of m_stuff*/ }
const list<T>& MyStuff() { return my_stuff; } //*Hate* this, but see class C
private:
Mutex mutex; //Goes in the *Item methods, but is largely worthless in MyStuff()
list<T> my_stuff; //Just as well a vector or deque
};
extern A a; //defined in the .cpp file
class B
{
...
void SomeFunction() { ... a.RemoveItem(item); }
};
class C
{
...
void IterateOverStuff()
{
const list<T>& my_stuff(a.MyStuff());
for (list<T>::const_iterator it=my_stuff.begin(); it!=my_stuff.end(); ++it)
{
...
}
}
};
De nouveau, B::SomeFunction()
et C::IterateOverStuff()
sont appelé de manière asynchrone. Est-ce qu'une structure de données que je peux utiliser pour s'assurer que lors de l'itération, my_stuff
est "protégé" d'ajouter ou de supprimer des opérations?
OriginalL'auteur Matt Phillips | 2011-04-14
Vous devez vous connecter pour publier un commentaire.
sonne comme un verrou de lecture/écriture est nécessaire. Fondamentalement, l'idée est que vous pouvez avoir 1 ou plusieurs lecteurs OU un seul écrivain. Ne pouvez jamais vous avez une lecture et d'écriture de verrouillage en même temps.
EDIT: Un exemple d'utilisation qui je pense correspond à votre conception implique de faire un petit changement. Ajouter un "itérer" la fonction de la classe qui possède la liste et la rendre basé sur un modèle de sorte que vous pouvez passer une fonction/foncteur de définir ce qu'il faut faire pour chaque nœud. Quelque chose comme ceci (rapide et sale de pseudo-code, mais vous obtenez le point...):
Une autre Option serait de tirer le verrou de lecture/écriture accessible au public et demander à l'appelant de responsable de la bonne utilisation de la serrure. Mais c'est plus sujette aux erreurs.
Je ne comprends pas très bien, où aurais-je la mettre? Il suffit de définir à l'échelle mondiale et le mettre dans C::Itérer... ainsi que l'Une des méthodes? Comment est-il "sait" qu'il s'agisse de lecture ou d'écriture est effectuée
il devrait avoir le même champ d'application de la propriété que la liste elle-même. Si la liste est global, la serrure doit être globale. Si un objet est propriétaire de la liste, alors que le même objet propre de la serrure.
Votre solution dans le modifier serait génial sauf pour les cas, comme le mien, malheureusement, où pred devrait être quelque chose de très compliqué (quand il y a beaucoup de choses qui se passent entre les crochets dans la boucle for). Plus précisément où pred est d'accéder aux données privées des membres de C. Pour cela, j'aurais mondialiser pred et de passer une instance de la classe C (et probablement en faire un ami), est ce que le droit? Dans ce cas, recommanderiez-vous votre deuxième option?
non, dans mon exemple, j'ai utilisé le boost de se lier à passer un fonction de C comme le Pred. Il sera appelé avec la bonne
this
et tout et tout.OriginalL'auteur Evan Teran
À mon humble avis c'est une erreur privé mutex dans une structure de données de la classe, puis d'écrire les méthodes de la classe afin que la chose entière est thread-safe n'importe ce que le code qui appelle les méthodes. La complexité qui est nécessaire pour ce faire complètement et parfaitement est beaucoup au-dessus.
La manière la plus simple est d'avoir un public ( ou global ) mutex qui le code appelant est responsable de verrouillage quand il a besoin d'accéder aux données.
Ici est mon article de blog sur ce sujet.
OriginalL'auteur ravenspoint
Lorsque vous revenez à la liste, de retour, il est enfermé dans une classe qui verrouille/déverrouille le mutex dans son constructeur/destructeur. Quelque chose le long des lignes de
La partie la plus délicate est écrit constructeur de copie, de sorte que votre mutex n'a pas à être récursive. Ou simplement l'utiliser auto_ptr.
Oh, et le verrou de lecture/écriture est en effet une solution meilleure que l'mutex ici.
Je préfère utiliser un bloc {LockedIterable l = ...;for(){...}}. Il gère la portée pour vous. Comme à ne pas avoir de membres de données - j'ai simplifié mon code, il dispose de données des membres list_ et mutex_. La copie est non-trivial si vous avez besoin de ne pas vouloir garder le verrouillage/déverrouillage du mutex et/ou ne veulent pas de verrouillage de la non-récursive mutex deux fois.
OriginalL'auteur Arkadiy