SynchronisationSingle-Writer, Multiple Readers Lock

Vous voudrez peut-être vous référer à verrous.html#zoo_mutex_cpp pour plus de détails sur les types de mutex disponibles en C++.

Il existe plusieurs cas où une même ressource peut être accédée sans incident par plusieurs fils d'exécution en lecture, mais par un seul fil d'exécution en écriture (écrire demande l'exclusivité, mais lire n'en exige pas autant).

Avec C++ 14, la modélisation de ce concept passe par un shared_timed_mutex et par une combinaison de shared_lock (pour permettre les accès concurrents en lecture) et de unique_lock (dans le cas des écritures). Ceci permet des accès concurrents en lecture, et peux accroître le débit des programmes quand le contexte s’y prête.

Voici un exemple de synchronisation sur un objet pour plusieurs accès concurrents en lecture, mais un seul en écriture :

L'accès en lecture utilise un shared_lock sur un shared_timed_mutex (ou un shared_mutex, si vous le préférez et si vous avez un compilateur C++ 17 sous la main), ce qui permet à plusieurs fils d'exécution de partager un accès dans read_access() sur un même verrou.

Par contre, l'accès en écriture utilise unique_lock sur le même mutex, et demande ce faisant l'accès exclusif à la ressource protégée par ce mutex.

Ainsi, on accroît le débit du code concurrent en permettant de véritables lectures concurrentes.

Notez que les types de mutex n'ont plus à être spécifiés explicitement depuis C++ 17 dû à CTAD. Par exemple, ceci :

shared_lock<shared_mutex> _{ mut };

... peut maintenant s'écrire :

shared_lock _{ mut };
// C++ 14
class Ressource {
  mutable shared_timed_mutex mut;
  Data data;
public:
  template <class F>
    void read_access(F f) const {
      // lecture; accès partagé
      shared_lock<shared_timed_mutex> _{ mut };
      f(data); // lecture; data est const
    }
  template <class F>
    void update(F f) {
      // écrire demande un accès exclusif
      unique_lock<shared_timed_mutex> _{ mut };
      f(data); // écriture; data est modifiable
    }
};
// C++ 17
class Ressource {
  mutable shared_mutex mut;
  Data data;
public:
  template <class F>
    void read_access(F f) const {
      // lecture; accès partagé
      shared_lock<shared_mutex> _{ mut };
      f(data); // lecture; data est const
    }
  template <class F>
    void update(F f) {
      // écrire demande un accès exclusif
      unique_lock<shared_mutex> _{ mut };
      f(data); // écriture; data est modifiable
    }
};

Autre chic exemple, qui permet une synchronisation correcte de deux objets partagés lors d'une opération d'affectation :

// emprunté à cppreference, inclure <mutex> et <shared_mutex>
class R {
   mutable shared_mutex m;
   // diverses données à synchroniser
public:
   R& operator=(const R &autre) {
      // il faut un accès exclusif pour modifier *this
      unique_lock<shared_mutex> gauche{ m, defer_lock };
      // il faut un accès partagé pour lire autre
      shared_lock<shared_mutex> droite{ autre.m, defer_lock };
      lock(gauche, droite); // le verrouillage effectif se fait ici
      // modifier les états de *this
      return *this;
   }
};

Valid XHTML 1.0 Transitional

CSS Valide !