La question cachée est: où est-ce que le mutex la protection de votre classe?
En résumé, disons que vous souhaitez lire le contenu d'un objet qui est protégée par un mutex.
La "lecture" de la méthode doit être sémantiquement "const" car il ne modifie pas l'objet lui-même. Mais à la lecture de la valeur, vous avez besoin de verrouiller un mutex, extraire la valeur, puis déverrouille le mutex, sens le mutex doit lui-même être modifié, ce qui signifie le mutex lui-même ne peut pas être "const".
Si le mutex est externe
Ensuite, tout est ok. L'objet peut être "const", et le mutex n'avez pas besoin de l'être:
Mutex mutex ;
int foo(const Object & object)
{
Lock<Mutex> lock(mutex) ;
return object.read() ;
}
À mon humble avis, c'est une mauvaise solution, car n'importe qui pourrait réutiliser le mutex pour protéger quelque chose d'autre. Vous y compris. En fait, vous trahir vous-même parce que, si votre code est assez complexe, vous aurez juste être confus au sujet de ce que le mutex est exactement protéger.
Je sais: j'ai été victime de ce problème.
Si le mutex est interne
Pour l'encapsulation, vous devez mettre le mutex aussi près que possible de l'objet, il est la protection.
Habituellement, vous allez écrire une classe avec un mutex à l'intérieur. Mais tôt ou tard, vous aurez besoin de protéger certaines complexe STL structure, ou quelque chose écrit par un autre sans mutex l'intérieur (ce qui est une bonne chose).
Une bonne façon de le faire est de calculer l'objet d'origine avec un modèle hériter d'ajouter le mutex fonctionnalité:
template <typename T>
class Mutexed : public T
{
public :
Mutexed() : T() {}
// etc.
void lock() { this->m_mutex.lock() ; }
void unlock() { this->m_mutex.unlock() ; } ;
private :
Mutex m_mutex ;
}
De cette façon, vous pouvez écrire:
int foo(const Mutexed<Object> & object)
{
Lock<Mutexed<Object> > lock(object) ;
return object.read() ;
}
Le problème est qu'il ne fonctionne pas car object
est const, et le verrou de l'objet est l'appel de la non-const lock
et unlock
méthodes.
Le Dilemme
Si vous croyez const
est limitée au niveau du bit const objets, puis, vous êtes foutu, et doit revenir à la "externes mutex solution".
La solution est d'admettre const
est plus d'une sémantique de qualificateur (qu'est - volatile
lorsqu'il est utilisé comme une méthode de qualifier de classes). Vous êtes à cacher le fait que la classe n'est pas entièrement const
mais encore assurez-vous de fournir une implémentation qui maintient la promesse que le des parties significatives de la classe ne sera pas modifié lors de l'appel d'un const
méthode.
Vous devez alors déclarer votre mutex mutable, et le verrouillage/déverrouillage des méthodes d' const
:
template <typename T>
class Mutexed : public T
{
public :
Mutexed() : T() {}
// etc.
void lock() const { this->m_mutex.lock() ; }
void unlock() const { this->m_mutex.unlock() ; } ;
private :
mutable Mutex m_mutex ;
}
L'interne mutex solution est bonne à mon humble avis: le fait d'Avoir des objets déclaré l'un près de l'autre dans une main, et de les avoir à la fois agrégées dans une enveloppe dans l'autre main, est la même chose à la fin.
Mais l'agrégation suivantes pour:
- C'est plus naturel (vous pouvez verrouiller l'objet avant d'y accéder)
- Un objet, un mutex. Comme le code de style vous oblige à suivre ce modèle, il diminue les risques de blocage en raison d'un mutex permettra de protéger un seul objet (et non pas plusieurs objets vous ne me souviens pas vraiment), et un seul objet sera protégée par un mutex (et non par de multiples mutex qui doit être verrouillé dans le bon ordre)
- Le mutexed classe ci-dessus peut être utilisé pour n'importe quelle classe
Alors, gardez votre mutex aussi près que possible de la mutexed objet (par exemple, à l'aide de la Mutexed construction ci-dessus), et aller de l' mutable
qualificatif pour le mutex.
Edit 2013-01-04
Apparemment, Herb Sutter ont le même point de vue: Sa présentation sur le "nouveau" sens de l' const
et mutable
en C++11 est très éclairant:
http://herbsutter.com/2013/01/01/video-you-dont-know-const-and-mutable/