10
votes

Comment iTerez-vous sur un conteneur de manière sécurisée?

J'ai un conteneur (C ++) sur lequel j'ai besoin de fonctionner de deux manières, de différents threads: 1) Ajouter et supprimer des éléments et 2) ITERER à ses membres. Clairement, éliminer l'élément tandis que l'itération se produit = désastre. Le code ressemble à ceci comme suit: xxx

à nouveau, B :: Asscunction () et c :: itétryoverStuff () est appelé asynchroneusement. Quelle est une structure de données que je peux utiliser pour vous assurer que lors de l'itération, my_stuff est "protégé" à partir d'ajouter ou de supprimer des opérations?


0 commentaires

3 Réponses :


15
votes

sonne comme un Verrou de lecteur / écrivain est nécessaire. Fondamentalement, l'idée est que vous puissiez avoir 1 ou plusieurs lecteurs ou un seul écrivain. Ne pouvez-vous jamais avoir un verrou de lecture et d'écriture en même temps.

EDIT: Un exemple d'utilisation que je pense convient à votre conception implique de faire un petit changement. Ajoutez une fonction "itérate" à la classe qui détient la liste et le rendez-le, de sorte que vous puissiez transmettre une fonction / fonction pour définir quoi faire pour chaque nœud. Quelque chose comme ceci (pseudo rapide et sale, mais vous obtenez le point ...): xxx

une autre option serait de faire le lecteur / Serrure écrivain accessible au public et dispose de l'appelant responsable d'utiliser correctement la serrure. Mais c'est plus sujet d'erreur.


7 commentaires

+1. Boost a une implémentation de ceux-ci: boost.org/doc/libs/1_46_1/doc/html/thread/...


@Évan, @Larsmans je ne comprends pas tout à fait - oùais-je le mettre? Il suffit de le définir globalement et de la mettre en C :: itérer ... ainsi que les méthodes? Comment est-il "savoir" si lu ou écrit est en cours d'exécution


@Matt: Cela devrait probablement avoir la même portée / propriété que la liste elle-même. Si la liste est globale, la serrure doit être globale. Si un objet possède la liste, alors que le même objet devrait posséder la serrure.


@Évan Votre solution dans la modification serait grande sauf pour les cas, comme la mienne malheureusement, où Pred devrait être quelque chose de très compliqué (quand il y a beaucoup de choses qui se passent entre les crochets de la boucle de la boucle). Spécifiquement où Pred accède aux membres de données privés de C. Pour cela, je devrais globaliser et transmettre une instance de classe C (et en faire probablement un ami), est-ce correct? Donc, dans ce cas, recommanderiez-vous votre deuxième option?


@Matt: Non, dans mon exemple, j'ai utilisé Boost Bind Pour passer une fonction de membre de C comme prédée prédée. Il sera appelé avec le bon ce et tout.


@Évan Ok Great ça sonne fantastique, je n'ai jamais utilisé de boost avant, mais peut-être maintenant est le moment de commencer. Dernière question, alors: la liaison à Boost a-t-elle des coûts cachés, au-delà de la taille supplémentaire évidente de mon programme? Est-ce que je suis de faire quelque chose avec Boost, je pourrais regretter plus tard?


La plupart des boost sont «en-tête seulement», ce qui en fait une partie, il ne signifie pas que vous devez créer un lien vers une énorme bibliothèque.



3
votes

Lorsque vous retournez la liste, retournez-la dans une classe qui verrouille / déverrouille le mutex dans son constructeur / destructeur. Quelque chose le long des lignes de xxx

La partie délicate consiste à écrire Copy Constructor de sorte que votre mutex ne soit pas nécessaire pour être récursif. Ou utilisez simplement Auto_Ptr.

OH, et le verrou de lecteur / écrivain est en effet une meilleure solution que mutex ici.


2 commentaires

Merci, j'avais pensé à quelque chose dans ces lignes. La seule chose est que cela me requiert de détruire ensuite le verrouivable après la fin de l'itération. S'il y a beaucoup plus d'événement dans C :: itétryoverStuff après la complète de l'itération, il suffit d'attendre que cela ne soit pas suffisant, cela ne suffit pas ... Donc, je suppose que New / Supprimer est l'option ici? Enfin, la structure de copie n'est-elle pas triviale, car LockeDiterABLABLE n'a pas de membres de données?


Je préférerais utiliser un bloc supplémentaire {verrouillable L = ...; pour () {... ...}}. Cela gère la portée pour vous. Quant à ne pas avoir de membres de données - j'ai trop simplifié mon code - il a des membres de données LIST_ et MUTEX_. La copie est non triviale si vous avez besoin de ne pas vouloir bloquer / déverrouiller le mutex et / ou ne voulez pas verrouiller deux fois le mutex non récursif.



5
votes

IMHO C'est une erreur d'avoir un mutex privé dans une classe de structure de données, puis d'écrire les méthodes de classe afin que tout le fil soit en sécurité, quel que soit le code qui appelle les méthodes. La complexité nécessaire pour le faire complètement et parfaitement sur le dessus.

La manière plus simple est d'avoir un mutex public (ou global) que le code d'appel est responsable du verrouillage lorsqu'il doit accéder aux données.

ici est mon Article de blog sur ce sujet.


1 commentaires

Merci. Point a pris la réalité de la copie et des difficultés cors.