8
votes

Pourquoi passer Mutex comme paramètre à une fonction appelée par un thread?

À certains endroits, j'ai vu des personnes créer une piscine de fil et créer des fils et exécuter une fonction avec ces threads. Tout en appelant cette fonction Boost :: Mutex est adopté par référence. Pourquoi cela est-il fait? Je pense que vous pouvez avoir un mutex déclaré dans la fonction appelée elle-même ou peut être déclarée membre de la classe ou mondiale. Quelqu'un peut-il s'il vous plaît expliquer?

E.g. xxx

alors, xxx


1 commentaires

Vous devez ajouter un exemple particulier là où cela est utilisé. Il est probablement probablement dû à la sémantique implémentée dans l'exemple et ne peut pas être répondu sans contexte (sauf si vous ne voulez que le générique juste parce que ou peut avoir un sens Réponses)


3 Réponses :


11
votes

Les mutex sont des objets non copieux et, bien qu'ils puissent être membres d'une classe, cela compliquerait grandement la capacité de la copie de la classe mère. Ainsi, une méthode préférée, si un certain nombre d'instances de classe doivent partager les mêmes données, seraient à créer le mutex comme membre de données statique. Sinon, si le mutex n'a besoin que d'être verrouillé dans une instance de la classe elle-même, vous pouvez créer un pointeur sur un mutex comme membre de données non statique, puis chaque copie de la classe aurait son propre mutex alloué de manière dynamique (et rester copieux si c'est une exigence).

Dans l'exemple de code ci-dessus, ce qui se passe essentiellement est un mutex global étant passé dans le bassin de fil par référence. Cela permet à tous les threads partageant les mêmes emplacements de mémoire de créer une serrure exclusive sur cette mémoire en utilisant exactement le même mutex, mais sans la surcharge de devoir gérer l'aspect non copieux du mutex lui-même. Le mutex de cet exemple de code aurait également pu être un membre de données statique de la classe myClass plutôt qu'un mutex global qui est transmis par référence, l'hypothèse étant que chaque thread aurait besoin de verrouiller une certaine mémoire que est globalement accessible à partir de chaque fil.

Le problème avec un mutex local est que ce n'est qu'une version accessible localement du mutex ... Par conséquent, lorsqu'un thread verrouille le mutex afin de partager certaines données accessibles à l'échelle mondiale, les données elles-mêmes ne sont pas protégées, car tous les autres threads aura son propre mutex local qui peut être verrouillé et déverrouillé. Il défait tout le point d'exclusion mutuelle.


4 commentaires

Cela n'a aucun sens, il existe de nombreuses applications où les mautexes étant des membres non statiques ont un sens. Beaucoup plus souvent que d'avoir le mutex comme membre statique, et il n'y a aucune raison de ne pas stocker un pointeur ou une référence en tant que membre. Le mutex devrait être au niveau des données protégées (c'est-à-dire s'il protège les données statiques, elle doit être statique, si elle protège les attributs des membres, il devrait probablement être membre, s'il est partagé par différentes instances ou différent. Types, alors il doit être encore plus général


Désolé, je ne voulais pas dire que vous ne pouvez pas le faire ... Je voulais juste dire que si vous en faites un membre de données non statique, la création d'une classe correctement copieuse est beaucoup plus difficile. (En fin de compte, cela ne sera pas vraiment coché, vous devrez stocker un pointeur sur un mutex, puis la réaffecter et réaffecter le mutex dans la copie, ce qui n'est pas une copie «vraie» de chaque membre de la classe). Je vais réviser ma réponse pour rendre cela plus clair.


... tout dépend de la définition de votre objet (sémantiquement) dans la plupart des cas, le mutex ne fait pas partie de l'état de l'objet, mais est une utilité pour que le véritable état de la classe ne soit pas vu dans un état intermédiaire incorrect (invariants cassé) lorsqu'il est utilisé dans un environnement multithreau.


Oui, je suis d'accord, mais dans l'exemple de code donné, alors qu'il n'était pas explicitement exprimé, il semble que, comme un pointeur sur une méthode statique myClass a été transmis à lier sans Un pointeur à une instance de myClass , certaines données sont partagées entre des threads qui nécessiteraient un mutex accessible de plus d'une seule instance de myClass .



0
votes

Utilisation de Mutex local est incorrect: la piscine de thread peut invoquer plusieurs instances de fonction et ils doivent fonctionner avec le même mutex. Membre de la classe est ok. Passer mutex à la fonction le rend plus générique et lisible. L'appelant peut décider quel mutex passe: membre de la classe ou autre chose.


1 commentaires

Cela me frotte mal. Si c'est à la hauteur de la classe d'appel Comment utiliser le mutex, il ne doit pas faire partie de la définition de l'interface?



1
votes

Je pense que vous pouvez avoir un mutex déclaré dans la fonction appelée elle-même ou peut être déclarée membre de la classe ou mondiale. Quelqu'un peut-il s'il vous plaît expliquer? P>

Créer un nouveau mutex à l'entrée ne protège rien. P>

Si vous envisagez de déclarer un mutex statique (ou global) pour protéger les membres non statiques, vous pouvez également écrire le programme comme un seul Programme fileté (OK, il y a des cas d'angle). Une serrure statique bloquerait toutes les filetages mais une (assumant du concours); Il est équivalent à "un maximum d'un thread peut fonctionner dans le corps de ce procédé à une fois". La déclaration d'un mutex statique pour protéger les données statiques va bien. Comme David Rodriguez - Dribesas le libila succinctement dans les commentaires de la réponse: "Le mutex devrait être au niveau des données protégées". p>

vous peut em> déclarer une variable de membre Par exemple, qui prendrait la forme généralisée: p> xxx pré>

cette approche va bien, et dans certains cas idéal em>. C'est un sens dans la plupart des cas lorsque vous construisez un système dans lequel des instances ont l'intention de résumez la mécanique de verrouillage et des erreurs de leurs clients. Un inconvénient est que cela peut nécessiter davantage d'acquisitions et nécessite généralement des mécanismes de verrouillage plus complexes (par exemple réentrant). Par plus d'acquisitions: le client peut savoir qu'une instance est utilisée dans un seul thread: pourquoi verrouiller du tout dans ce cas? De plus, un groupe de petites méthodes de threadsafe introduira beaucoup de frais généraux. Avec le verrouillage, vous voulez entrer et sortir des zones protégées ASAP (sans introduire de nombreuses acquisitions), les sections critiques sont donc souvent des opérations plus grandes que typiques. p>

si l'interface publique nécessite cette serrure sous forme d'argument (comme on le voit dans votre exemple), il s'agit d'un signal que votre conception peut être simplifiée en privatisant le verrouillage (rendant la fonction d'objet de manière sécurisée de fil, plutôt que de transmettre la serrure comme une ressource tenue externe) . P>

à l'aide d'une serrure externe (ou liée ou associée), vous pouvez potentiellement réduire les acquisitions (ou le temps total verrouillé). Cette approche vous permet également d'ajouter le verrouillage à une instance après le fait. Il permet également au client de configurer la manière dont la serrure fonctionne. Le client peut utiliser moins de serrures en les partageant (parmi un ensemble d'instances). Même un exemple simple de composition peut illustrer ceci (supporter les deux modèles): P>

class t_composition {
public:
    ...
private:
    t_lock d_lock; // << name and data can share this lock
    t_string d_name;
    t_data d_data;
};


2 commentaires

Merci Justin. Mais cela signifie-t-il que si vous souhaitez protéger les données statiques, vous avez besoin d'un mutex statique et si vous souhaitez protéger le membre de données, vous avez besoin de niveau de classe Mutex en règle générale?


En tant qu'approche de Général : Oui, c'est l'une des approches utilisables multiples pour garantir que vos implémentations soient en sécurité (une autre étant l'approche de l'OP). La granularité est également importante si vous souhaitez minimiser les acquisitions et les temps de verrouillage. Un verrou de niveau de classe peut être trop petit dans certains cas, cela conduira à de nombreuses acquisitions inutiles. Un verrouillage plus vaste (global ou basé sur le document) ne sera pas idéal dans de nombreux cas car le temps verrouillé par acquisition augmente et il y a une probabilité beaucoup plus élevée que d'autres attendent pendant cette période.