7
votes

std :: vecteur push_back échoue lorsqu'il est utilisé dans un parallèle pour la boucle

J'ai un code qui est comme suit (code simplifié): xxx

Ce code fonctionne bien, mais si je veux le faire parallèlement en utilisant OMM parallèle pour, je reçois une erreur sur sortie.push_back et il semble que lors du redimensionnement du vecteur, la mémoire corrompue.

Quel est le problème et comment puis-je résoudre le problème?

Comment puis-je assurer un seul fil d'insertion nouvel article dans le vecteur à tout moment?


7 commentaires

Utilisez-vous des mécanismes de synchronisation?


@Luchiangriggore: Non! Le code est comme indiqué ci-dessus!


Les fonctions de non-const de std ne sont pas thread-coffre-fort; Vous devez synchroniser si vous souhaitez les modifier simultanément.


Vous avez de la chance que vous n'appeliez pas sortie.Reserve (saisie.Rows) . Cela aurait empêché la réaffectation de la mémoire, mais le code serait toujours un danger-danger. Et cela pourrait se manifester dans des valeurs «manquantes», ce qui serait beaucoup plus difficile à repérer.


La sortie doit-elle être commandée comme l'entrée? Garder cet ordre est un peu un défi lorsque l'entrée réelle n'est pas traitée dans l'ordre et qu'il n'y a pas de correspondance de 1 à 1 avec sortie [] . Selon les vitesses relatives de toutes les fonctions, il peut être utile de déterminer Goodinput [] séquentiellement, puis convertissez ce 1 à 1, en parallèle, à de sortie [] . Si isgoodmatch est le problème, calculez INPUTMASK [] en parallèle, puis faites le reste séquentiellement.


@Msalters: la sortie n'a pas besoin d'être dans le même ordre que l'entrée.


Certaines personnes, lorsqu'elles sont confrontées à un problème, pensez-moi, "je sais, je vais utiliser des threads", puis deux ils ont des efforts.


6 Réponses :


6
votes

std :: vecteur 's push_back ne peut pas garantir un comportement correct lorsqu'il est appelé de manière concomitante comme si vous faites maintenant (il n'y a pas de sécurité) .

Cependant, étant donné que les éléments ne dépendent pas l'un de l'autre, il serait très raisonnable de redimensionner le vecteur et modifier des éléments à l'intérieur de la boucle séparément: xxx

Étant donné que l'accès direct à l'aide de opérateur [] ne dépend pas d'autres membres de std :: vecteur qui pourrait causer des incohérences entre les fils. Cependant, Cette solution peut encore avoir besoin d'une synchronisation explicite (c'est-à-dire à l'aide d'un mécanisme de synchronisation telle que Mutex) qui garantira qu'une valeur correcte du k sera utilisée.

"Comment puis-je m'assurer qu'un seul fil insérant un nouvel élément dans le vecteur à tout moment?"

Vous n'avez pas besoin de. Les threads modifieront différents éléments (qui résident dans différentes parties de la mémoire). Il vous suffit de vous assurer que l'élément que chaque fil tente de modifier est le bon.


10 commentaires

Cela ne fonctionne pas car le nombre d'éléments de sortie n'est pas identique à l'entrée. S'il vous plaît regarder si (isgoodmatch ())


@mans: Voir mon édition. Vous pouvez toujours compter sur sortie.Size () <= entrée.Size ()


Vous devez toujours faire une sortie [k] = newvalues; k ++; atomique, ou il y a une condition de course, car deux threads peuvent tous deux écrire sur le même k .


@Antonguryanov: Bien que cela sonne parfaitement raisonnable, je pense que l'incrémentation d'une variable int de cette manière doit être traitée par OMP (mais je dois admettre que je ne suis pas sûr de)


@LIHO: Même si k ++ est atomique, il ne veut pas que si sortie [k] = newvalues ​​ est threadsafe et k ++ est atomique, alors sortie [k] = newvalues; k ++; est atomique. Deux threads pourraient tous les deux voir la même valeur de k dans la ligne du Fist avant l'un d'entre eux l'incromption dans la deuxième ligne.


@Stevejessop: Oui, je comprends le point d'Anton, mais ... êtes-vous sûr que cela n'est pas traité interne par OMP?


@Liho: Je ne suis pas certain que ce n'est pas, je n'ai jamais utilisé OMP. Mais cela semble être une prétention assez importante pour moi, que je considérerais improbable à moins que quelqu'un soit sûr. Voulez-vous dire que OMPT détectera toutes les utilisations de k dans la boucle et rendra le bloc de code du premier au dernier dans une opération atomique? Pour une boucle légèrement différente (une qui utilise k tôt dans le corps de la boucle) susceptible d'empêcher OMP de faire toute parallélisation du tout. Mais peut-être que c'est le point de déclarer k partagé, je ne sais pas ...


@Stevejessop: Oui, c'est ce que je soulignais ... qu'il pourrait y avoir une fonctionnalité plus complexe cachée derrière elle lorsque nous exprimons explicitement que k est partagé entre les threads. Cependant j'ai édité ma réponse :)


la bonne façon de capturer et d'incrémenter K


@Msalters: c'est un lien impressionnant :)



8
votes

La réponse simple est que std :: vecteur :: push_back n'est pas thread-coffre-fort.

Pour ce faire en toute sécurité en parallèle, vous devez synchroniser afin de vous assurer que push_back n'est pas appelé à partir de plusieurs threads en même temps.

Synchronisation en C ++ 11 peut facilement être obtenu en utilisant un std :: mutex .


4 commentaires

Pourquoi pas seulement mettre un #pragma omp critique avant le push_back?


@Daviddoria Je ne connais pas OpenMP et j'ai simplement donné la solution standard C ++.


@Daviddoria Votre suggestion a parfaitement fonctionné pour moi. Vous devriez en faire une réponse officielle


@Vinf. Je viens de poster la recommandation "critique" comme réponse.



-9
votes

Vous pouvez essayer d'utiliser un mutex pour résoudre le problème. Habituellement, je préfère réaliser une telle chose moi-même; xxx

espère que cela pourrait aider.


2 commentaires

Bonne démonstration de la raison pour laquelle vous ne devriez pas essayer d'écrire votre propre mutex


L'utilisation d'un mutex pour synchroniser les accès au vecteur est une approche plausible, mais la seule chose que votre code vous synchronise la température de la CPU avec le temps que votre programme est exécuté.



2
votes

Utilisez le vecteur simultané xxx

Concurrence :: Concurrent_vector en C ++ 11.

Il s'agit de la version sécurisée de fil Vecteur.


1 commentaires

Je ne vois pas aucune preuve n'importe où que concurrent_vector est "en C ++ 11". Il y en a une implémentation dans les blocs de construction de threading d'Intel comme TBB: Concurrent_vector et l'exécution de la concurrence de Microsoft (conclure) a AA Concurrence :: Concurrent_vector , mais ceux-ci sont spécifiques à Intel TBB et Visual C ++, respectivement. (OpenMP peut utiliser être utilisé dans MSVC, accordé. Mais Intel recommande de ne pas le mélanger avec TBB.)



2
votes

mettre un #pragma omp critique avant le push_back .


0 commentaires

0
votes

J'ai résolu un problème similaire en dérivant la classe std :: vecteur classement pour implémenter un atomic_push_back méthode, approprié pour fonctionner dans le openmp Paradigm.

Voici mon implémentation de vecteur "openmp-coffre-fort": xxx

Bien sûr, vous devez inclure omp.h . Ensuite, votre code pourrait être juste comme suit: xxx

si vous avez toujours besoin de la sortie de la sortie ailleurs dans une section non parallèle du code, Vous pouvez simplement utiliser la méthode normale push_back .


0 commentaires