Les interfaces des conteneurs n'ont tout simplement pas été conçues dans cet objectif. Pour les interfaces qu'ils utilisent, un verrou visible par le client est vraiment le seul moyen d'y parvenir tout en garantissant la correction et un comportement prévisible. Ce serait également terriblement inefficace car le nombre d'acquisitions serait très élevé (par rapport à une bonne implémentation).
Solution 1
Pass by value (le cas échéant).
Solution 2
Créez une collection d'implémentations simples que vous pouvez utiliser pour passer des conteneurs tout en gardant un verrou de portée (considérez cela comme du pseudo c++) :
template <typename TCollection>
class t_locked_collection {
public:
t_locked_collection(TCollection& inCollection, t_lock& lock) : collection(inCollection), d_lock(lock), d_nocopy() {
}
TCollection& collection;
// your convenience stuff
private:
t_scope_lock d_lock;
t_nocopy d_nocopy;
};
L'appelant associe alors le verrou à la collection, puis vous mettez à jour vos interfaces pour utiliser (passer par) le type de conteneur le cas échéant. C'est juste une extension de classe du pauvre.
Ce conteneur verrouillé est un exemple simple, et il existe quelques autres variantes. C'est la voie que j'ai choisie parce qu'elle vous permet vraiment d'utiliser le niveau de granularité qui est idéal pour votre programme, même si elle n'est pas aussi transparente (syntaxiquement) que les méthodes verrouillées. C'est aussi relativement facile d'adapter des programmes existants. Au moins, il se comporte d'une manière prévisible, contrairement aux collections avec des verrous internes.
Une autre variante serait :
template <typename TCollection>
class t_lockable_collection {
public:
// ...
private:
TCollection d_collection;
t_mutex d_mutex;
};
// example:
typedef t_lockable_collection<std::vector<int> > t_lockable_int_vector;
...où un type similaire à t_locked_collection
pourrait être utilisé pour exposer la collection sous-jacente. Je ne veux pas dire que cette approche est infaillible, mais simplement résistante.
2 votes
+Scott Meyers a posé cette question à l'époque de C++0x. aquí Il serait intéressant de savoir comment cela a changé après C++11.
0 votes
Il est très facile de transformer une file d'attente standard en une file d'attente bloquante à l'aide de primitives.