Exemple pour boost shared_mutex (lectures multiples / une écriture)?

J’ai une application multithread qui doit lire certaines données souvent, et occasionnellement ces données sont mises à jour. En ce moment, un mutex garde l’access à ces données en toute sécurité, mais c’est cher car j’aimerais que plusieurs threads puissent lire simultanément et ne les verrouiller qu’une fois qu’une mise à jour est nécessaire (le thread de mise à jour peut attendre que les autres threads se terminent) .

Je pense que c’est ce que boost::shared_mutex est supposé faire, mais je ne sais pas comment l’utiliser, et je n’ai pas trouvé d’exemple clair.

Quelqu’un at-il un exemple simple que je pourrais utiliser pour commencer?

On dirait que tu ferais quelque chose comme ça:

 boost::shared_mutex _access; void reader() { // get shared access boost::shared_lock lock(_access); // now we have shared access } void writer() { // get upgradable access boost::upgrade_lock lock(_access); // get exclusive access boost::upgrade_to_unique_lock uniqueLock(lock); // now we have exclusive access } 

1800 INFORMATION est plus ou moins correcte, mais il y a quelques problèmes que je voulais corriger.

 boost::shared_mutex _access; void reader() { boost::shared_lock< boost::shared_mutex > lock(_access); // do work here, without anyone having exclusive access } void conditional_writer() { boost::upgrade_lock< boost::shared_mutex > lock(_access); // do work here, without anyone having exclusive access if (something) { boost::upgrade_to_unique_lock< boost::shared_mutex > uniqueLock(lock); // do work here, but now you have exclusive access } // do more work here, without anyone having exclusive access } void unconditional_writer() { boost::unique_lock< boost::shared_mutex > lock(_access); // do work here, with exclusive access } 

Notez également que, contrairement à shared_lock, seul un seul thread peut acquérir un upgrade_lock à la fois, même s’il n’est pas mis à niveau (ce qui m’a semblé gênant lorsque je l’ai rencontré). Donc, si tous vos lecteurs sont des rédacteurs conditionnels, vous devez trouver une autre solution.

Vous pouvez utiliser boost pour créer un verrou en lecture-écriture:

 #include  #include  typedef boost::shared_mutex Lock; typedef boost::unique_lock< Lock > WriteLock; typedef boost::shared_lock< Lock > ReadLock; Lock myLock; void ReadFunction() { ReadLock r_lock(myLock); //Do reader stuff } void WriteFunction() { WriteLock w_lock(myLock); //Do writer stuff } 

Juste pour append quelques informations empiriques, j’ai étudié toute la question des verrous évolutifs, et Exemple pour boost shared_mutex (lectures multiples / une écriture)? est une bonne réponse en ajoutant les informations importantes qu’un seul thread peut avoir un upgrade_lock même s’il n’est pas mis à niveau, ce qui est important car cela signifie que vous ne pouvez pas passer d’un verrou partagé à un verrou unique sans libérer d’abord le verrou partagé. (Ceci a été discuté ailleurs mais le fil le plus intéressant est ici http://thread.gmane.org/gmane.comp.lib.boost.devel/214394 )

Cependant, j’ai trouvé une différence importante (non documentée) entre un thread en attente d’une mise à niveau vers un verrou (c.-à-d. Il doit attendre que tous les lecteurs libèrent le verrou partagé) et un verrou d’écriture attendant la même chose (un unique_lock).

  1. Le thread qui attend un unique_lock sur le shared_mutex bloque tout nouveau lecteur entrant, il doit attendre la requête des écrivains. Cela garantit que les lecteurs ne meurent pas de faim (cependant, je pense que les écrivains pourraient affamer les lecteurs).

  2. Le thread qui attend un upgradeeable_lock pour effectuer la mise à niveau permet à d’autres threads d’obtenir un verrou partagé, de sorte que ce thread pourrait être affamé si les lecteurs sont très fréquents.

Ceci est une question importante à considérer et devrait probablement être documentée.

Utilisez un sémaphore dont le nombre est égal au nombre de lecteurs. Laissez chaque lecteur prendre un compte du sémaphore pour pouvoir lire, de cette façon, ils peuvent tous lire en même temps. Ensuite, laissez l’auteur prendre TOUT le sémaphore compte avant d’écrire. Cela oblige le rédacteur à attendre que toutes les lectures se terminent, puis à bloquer les lectures lors de l’écriture.

Bonne réponse de Jim Morris, je suis tombé sur cela et cela m’a pris du temps à comprendre. Voici un code simple qui montre qu’après avoir soumis une “demande” pour un boost unique_lock (version 1.54), bloque toutes les requêtes shared_lock. Ceci est très intéressant car il me semble que choisir entre unique_lock et upgradeable_lock permet si on veut écrire en priorité ou sans priorité.

Aussi (1) dans le post de Jim Morris semble contredire ceci: Boost shared_lock. Lire préféré?

 #include  #include  using namespace std; typedef boost::shared_mutex Lock; typedef boost::unique_lock< Lock > UniqueLock; typedef boost::shared_lock< Lock > SharedLock; Lock tempLock; void main2() { cout << "10" << endl; UniqueLock lock2(tempLock); // (2) queue for a unique lock cout << "11" << endl; boost::this_thread::sleep(boost::posix_time::seconds(1)); lock2.unlock(); } void main() { cout << "1" << endl; SharedLock lock1(tempLock); // (1) aquire a shared lock cout << "2" << endl; boost::thread tempThread(main2); cout << "3" << endl; boost::this_thread::sleep(boost::posix_time::seconds(3)); cout << "4" << endl; SharedLock lock3(tempLock); // (3) try getting antoher shared lock, deadlock here cout << "5" << endl; lock1.unlock(); lock3.unlock(); }