2010-12-03 28 views
11

Disons que j'ai un vecteur de N éléments, mais jusqu'à n éléments de ce vecteur ont des données significatives. Un thread de mise à jour met à jour le nième ou n + 1er élément (puis définit n = n + 1), vérifie également si n est trop proche de N et appelle vectoriel :: resize (N + M) si nécessaire. Après la mise à jour, le thread appelle plusieurs threads enfants pour lire jusqu'à nième données et effectuer des calculs.Le vecteur STL et la sécurité des threads

Il est garanti que les threads enfants ne modifient ou ne suppriment jamais de données (en fait, aucune donnée n'est supprimée) et que le programme de mise à jour appelle les enfants juste après la fin de la mise à jour. Jusqu'à présent, aucun problème n'est survenu, mais je voudrais savoir si un problème peut survenir lors de la réaffectation d'un vecteur à un bloc de mémoire plus grand, s'il reste des fils de travail pour enfants à partir de la mise à jour précédente.
Ou est-il sûr d'utiliser un vecteur, car il n'est pas thread-safe, dans un tel cas multithread?

EDIT: Étant donné que seule l'insertion a lieu lorsque le programme de mise à jour appelle vector :: resize (N + M, 0), y a-t-il des solutions possibles à mon problème? En raison de la grande performance du vecteur STL je ne suis pas disposé à le remplacer par un vecteur verrouillable ou dans ce cas existe-t-il des vecteurs performants, connus et sans verrou?

Répondre

19

Je souhaite savoir si un problème peut survenir lors de la réaffectation d'un vecteur à un bloc de mémoire plus grand, s'il reste des threads de travail enfant à partir de la mise à jour précédente.

Oui, ce serait très mauvais.

Si vous utilisez un conteneur à partir de plusieurs threads et qu'au moins un thread peut effectuer une action susceptible de modifier l'état du conteneur, l'accès au conteneur doit être synchronisé.

Dans le cas de std::vector, tout ce qui change sa taille (notamment, insertions et effacements) changer son état, même si une réallocation n'est pas nécessaire (l'insertion ou l'effacement nécessite des données de comptabilité de taille internes de std::vector à être mis à jour) .


Une solution à votre problème serait d'avoir le producteur d'allouer dynamiquement la std::vector et utiliser un std::shared_ptr<std::vector<T> > de posséder et donner ce std::shared_ptr à chacun des consommateurs.

Lorsque le producteur a besoin d'ajouter plus de données, il peut allouer dynamiquement une nouvelle std::vector avec une nouvelle, plus grande taille et des copies des éléments de l'ancien std::vector. Ensuite, lorsque vous créez de nouveaux consommateurs ou mettez à jour les consommateurs avec les nouvelles données, il vous suffit de leur donner un std::shared_ptr au nouveau std::vector.

+0

McNellis @ James: Oui. C'est un bon conseil.Je peux faire la réaffectation moi-même. En fait, les vecteurs sont enveloppés dans une classe qui contient un pointeur sur le vecteur. Ce n'est pas shared_ptr mais je peux facilement construire un nouveau grand vecteur, copier des éléments de l'ancien, le supprimer. Alors, quel est le moyen le plus rapide pour copier un grand bloc de mémoire. CopyMemory()? –

+1

Une solution plus simple ne serait-elle pas d'utiliser 'std :: deque' au lieu d'un vecteur? Cela évite entièrement les réallocations, tout en offrant des performances proches du vecteur. – jalf

+0

@jalf: Je ne pense pas qu'il soit sûr d'utiliser un 'std :: deque' car les réaffectations ne sont pas la seule préoccupation. Il n'y a aucune garantie que 'std :: deque :: operator []' ne vérifie pas la taille ou toute autre comptabilité interne au 'deque', donc il y a un potentiel pour une condition de concurrence où un consommateur appelle' operator [] ', qui lit l'état interne pendant que le producteur ajoute des données, ce qui modifie l'état interne. –

1

Comment vos employés décident-ils de travailler sur le thread de données en toute sécurité? Y a-t-il une signalisation entre les travailleurs et le producteur? Si ce n'est pas le cas, il y a certainement un problème où le producteur pourrait faire bouger le vecteur pendant qu'il est encore en train d'être travaillé. Bien que cela puisse trivialement être corrigé en passant à un std::deque à la place. (Notez que std::deque invalide les itérateurs sur push_back mais les références aux éléments ne sont pas affectées).

+0

@stonemetal: Il n'y a pas de signalisation entre les travailleurs et le producteur. Comment vais-je utiliser Deque? –

+0

@stonemetal: Je n'insère pas de données avec push_back(). Je le redimensionne puis j'appelle 'vec [n] = X;' Est-ce important? –

+0

Cela dépend de la façon dont vous distribuez des emplois aux travailleurs. Vous dites que vous ne faites qu'ajouter à la fin, donc vous pouvez très bien utiliser push_back ou redimensionner comme nécessaire puis distribuer le travail aux index par des index qui ne changent jamais puisque vous ne poussez pas ou pop avant ou effacez, vous pouvez également distribuer le travail par un bloc de pointeurs puisque les éléments sont garantis pour ne pas bouger, bien qu'ils ne soient pas garantis contigus, vous aurez donc besoin d'un pointeur vers chaque élément dont un ouvrier a besoin plutôt que d'un tableau de début et de longueur. Les itérateurs sont invalidés lors du redimensionnement afin qu'ils ne soient pas une option. – stonemetal