1
votes

Insertion de vecteurs thread-safe C ++

Plusieurs threads traitent les données et à la fin, ils insèrent / ajoutent tous les résultats dans le vecteur RESULT.
Je veux m'assurer qu'un seul thread insère des éléments dans le vecteur RESULT à la fois.
Pour cela, j'ai utilisé mutex et lock_guard () mais ils donnent parfois une erreur d'accès invalide et parfois ils ne le font pas ...
Voici mon code pour synchroniser

void insert_output() {
    mutex m;
    lock_guard<mutex> lockGuard(m);
    Resources::results.insert(Resources::results.begin(), output.begin(), output.end());
}

Est-ce que je fais quelque chose de mal ...
Et quelle est la meilleure façon de synchroniser ce processus?


1 commentaires

Remarque: l'insertion au début d'un std :: vector toujours nécessite de copier / déplacer le vecteur entier. Cela prendra beaucoup de temps très rapidement. Vous ne devez insérer qu'à la fin d'un std :: vector . Si vous avez besoin d'une insertion au début, utilisez std :: deque ou std :: list ou insérez à la fin en sens inverse et inversez le vecteur par la suite.


3 Réponses :


1
votes

vous devez créer le mutex en une seule instance, il crée un verrou par instance.

mutex m;
void insert_output() {
    lock_guard<mutex> lockGuard(m);
    Resources::results.insert(Resources::results.begin(), output.begin(), output.end());
}


1 commentaires

D'accord, c'est bien, mais la question suivante est: devrais-je déclarer le mutex m comme une variable statique parce que comme vous l'avez dit "créer le mutex en une instance" pour éviter "un verrou par instance"?



3
votes

Il ne doit y avoir qu'un un mutex pour le vecteur. Vous devez donc ajouter le mutex à côté du vecteur, par exemple comme results_mutex dans Resources . Si results est un membre statique, alors le mutex doit également être un membre statique (afin qu'il n'y ait qu'un seul mutex pour le vecteur).

Ensuite, vous devez également verrouiller le mutex sur toutes les opérations accédant au vecteur qui pourraient potentiellement être exécutées en parallèle avec un appel à insert_output , pas seulement sur l'opération d'insertion.

Dans votre code actuel, vous créez un nouveau mutex à chaque appel, ce qui le rend totalement inutile.


4 commentaires

L'insertion est le seul endroit où n'importe quel thread utilise le vecteur de résultats ... tous les threads ont leur propre résultat de sortie pour stocker les résultats temporaires et une fois qu'ils sont terminés, ils insèrent la sortie dans le vecteur de résultats principal partagé. Donc, je m'assure que le vecteur de résultats n'est utilisé qu'à un seul endroit ... Je veux demander que devrais-je déclarer ce mutex comme variable statique pour m'assurer qu'il est partagé entre tous les threads comme je le fais avec le vecteur de résultats principal .


@xyzxyz Si tel est le cas, vous pouvez simplement déclarer m comme statique ou comme global en dehors de la fonction et en finir avec, oui, mais ce n'est pas une solution propre. Cela fonctionne uniquement parce que results est une variable statique et ne fonctionnerait pas correctement s'il s'agissait d'un membre non statique. Il est également plus facile de raisonner sur le code si le mutex est directement associé à l'objet qu'il protège.


Hé, dois-je mettre lock_guard dans la classe de ressources aussi ... en tant que membre statique?


@xyzxyz Non, le lock_guard doit toujours être une variable automatique dans le bloc dans lequel vous avez l'intention d'accéder à l'objet protégé (et déclaré avant l'accès réel). S'il y avait plusieurs de ces endroits, vous utiliseriez plusieurs lock_guard , mais toujours un seul mutex.



2
votes

Avec une légère amélioration de la spécification, cela devient assez simple. Si vous avez n threads qui créent chacun des résultats m , créez simplement votre vecteur avec des éléments n * m et demandez à chaque thread de copier ses résultats dans un sous-gamme appropriée. Ainsi, le thread j copie ses résultats dans résultats [j * m] via résultats [j * m + m - 1] . Aucune synchronisation nécessaire, car aucun des threads ne modifie simultanément le même élément.

De manière plus générale, si vous savez à l'avance combien de résultats chaque thread va créer, vous pouvez carreler le vecteur de résultat de manière appropriée afin qu'il n'y ait pas d'écritures conflictuelles.


0 commentaires