J'ai une carte comme variable membre et plusieurs threads qui accèdent à la carte (accès en lecture et en écriture). Maintenant, je dois m'assurer qu'un seul thread a accès à la carte. Mais comment puis-je le faire ? Quelle est la meilleure solution pour cela ?
Réponses
Trop de publicités?Boost contient de belles implémentations de verrous pour l'accès partagé. Jetez un coup d'œil à la documentation .
Dans votre cas, vous avez probablement besoin d'un verrou de lecture-écriture, car un verrou d'exclusion mutuelle est probablement excessif si vous avez beaucoup de lectures et très peu d'écritures.
Vous avez besoin de synchroniser l'accès à votre carte, par exemple en utilisant une Mutex POSIX . Le lien contient quelques exemples simples à suivre sur la façon d'utiliser les variables d'exclusion mutuelle.
En fait, le principe selon lequel un seul fil d'exécution doit accéder à l'espace de travail de l'ordinateur de l'entreprise n'est pas pertinent. map
à un moment donné est légèrement décalé.
Les lectures simultanées sont acceptables, ce que vous voulez éviter, c'est qu'un thread modifie la carte pendant que les autres la lisent.
Selon le niveau de granularité dont vous avez besoin, vous pouvez envisager un verrou lecteur/écrivain, qui permettra à plusieurs lectures de se dérouler en parallèle.
L'usage exact a été démontré ici en utilisant Boost :
boost::shared_mutex _access;
void reader()
{
// get shared access
boost::shared_lock<boost::shared_mutex> lock(_access);
// now we have shared access
}
void writer()
{
// get upgradable access
boost::upgrade_lock<boost::shared_mutex> lock(_access);
// get exclusive access
boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock);
// now we have exclusive access
}
Après cela, il s'agit simplement d'envelopper de manière pratique l'accès à la carte. Par exemple, vous pouvez utiliser une structure proxy générique :
template <typename Item, typename Mutex>
class ReaderProxy {
public:
ReaderProxy(Item& i, Mutex& m): lock(m), item(i) {}
Item* operator->() { return &item; }
private:
boost::shared_lock<Mutex> lock;
Item& item;
};
template <typename Item, typename Mutex>
class WriterProxy {
public:
WriterProxy(Item& i, Mutex& m): uplock(m), lock(uplock), item(i) {}
Item* operator->() { return &item; }
private:
boost::upgrade_lock<Mutex> uplock;
boost::upgrade_to_unique_lock<Mutex> lock;
Item& item;
};
Et vous pouvez les utiliser comme :
class Foo {
typedef ReaderProxy< std::map<int, int>, boost::shared_mutex> Reader;
typedef WriterProxy< std::map<int, int>, boost::shared_mutex> Writer;
public:
int get(int k) const {
Reader r(map, m);
auto it = r->find(k);
if (it == r->end()) { return -1; }
return it->second;
}
void set(int k, int v) {
Writer w(map, m);
w->insert(std::make_pair(k, v));
}
private:
boost::shared_mutex m;
std::map<int, int> map;
};
Attention toutefois aux itérateurs, ils ne peuvent être manipulés en toute sécurité que lorsque le mutex est détenu par le thread actuel.
De plus, je vous recommande de contrôler étroitement la carte, en la faisant entrer dans les plus petits objets qui ont un sens, et de ne fournir que les opérations dont vous avez besoin. Moins les méthodes ont accès à la carte, moins vous risquez de manquer un point d'accès.
Si vous avez un compilateur récent, vous pouvez utiliser std::mutex
(qui est basé sur l'implémentation de Boost). Cela fait partie de C++11, donc il n'est pas implémenté partout. gcc-4.6 fonctionne assez bien cependant. L'implémentation sous-jacente est constituée de threads POSIX sous linux et de threads Windows sous Windows.