wake-up-neo.com

unterschied zwischen std :: mutex und std :: shared_mutex

Ich bin auf einen std::shared_mutex in C++17 gestoßen. Was genau ist std::shared_mutex und wie unterscheidet es sich von std::mutex

8
asad_nitp

Wie in der Dokumentation vermerkt

Die shared_mutex-Klasse ist ein Synchronisationsprimitiv, das verwendet werden kann, um den gleichzeitigen Zugriff mehrerer Threads auf gemeinsam genutzte Daten zu verhindern. Im Gegensatz zu anderen Mutex-Typen, die den exklusiven Zugriff ermöglichen, verfügt ein shared_mutex über zwei Zugriffsebenen: 

  • shared - Mehrere Threads können sich den Besitz desselben Mutex teilen
  • exklusiv - Nur ein Thread kann den Mutex besitzen. 

Gemeinsam genutzte Mutexe werden normalerweise in Situationen verwendet, in denen mehrere Leser gleichzeitig auf dieselbe Ressource zugreifen können, ohne Datenrennen auszulösen. Dies kann jedoch nur ein Schreiber tun.

Dies hat eine Vielzahl von Verwendungsmöglichkeiten, aber eine übliche ist die Implementierung einer Lese-Schreibsperre , bei der mehrere Threads gemeinsam genutzte Daten lesen können, wobei jedoch immer nur ein Thread ausschließlich schreibt. Wenn Sie also mehrere Leser haben, befindet sich der Mutex im "gemeinsamen Modus", aber wenn ein Schreibzugriff angefordert wird, wechselt er in den "exklusiven Modus".

10
CoryKramer

std::shared_mutex kann nützlich sein, insbesondere in Fällen, in denen die Datenstruktur (wie der DNS-Cache) selten aktualisiert wird. Die Verwendung eines std::mutex zum Schutz der Datenstruktur kann zu pessimistisch sein, da die mögliche Parallelität beim Lesen der Datenstruktur Beseitigt wird, wenn sie nicht geändert wird. Mehrere Threads können gleichzeitig eine gemeinsame Sperre für denselben std::shared_mutex haben.

Ein solches Beispiel aus Anthony Williams Buch:

class dns_cache
{
    std::map<std::string,dns_entry> entries;
    mutable boost::shared_mutex entry_mutex;

public:

    dns_entry find_entry(std::string const& domain) const
    {
        boost::shared_lock<boost::shared_mutex> lk(entry_mutex);
        std::map<std::string,dns_entry>::const_iterator const it = entries.find(domain);
        return (it==entries.end()) ? dns_entry() : it->second;
    }

    void update_or_add_entry(std::string const& domain,
                            dns_entry const& dns_details)
    {
        std::lock_guard<boost::shared_mutex> lk(entry_mutex);
        entries[domain] = dns_details;
    }
};

Hier führt die Funktion find_entry im Wesentlichen die Leseoperation aus, während update_or_add_entry die Schreiboperation ausführt.

Es kann also gesagt werden, dass std::shared_mutex ein typischer Leser-Schreiber-Mutex ist, da Zwei verschiedene Arten der Verwendung möglich sind: exklusiver Zugriff durch einen einzelnen "Schreiber" -Thread oder geteilt, gleichzeitiger Zugriff durch mehrere "Reader" -Threads.

1
Saurav Sahu

Eine mutex ist entweder gesperrt oder nicht.

Ein shared_mutex ist entweder exklusiv gesperrt oder freigegeben oder nicht.

Beliebig viele Clients können gemeinsam einen gemeinsamen Mutex sperren.

Wenn jemand es exklusiv gesperrt hat, kann niemand anderes any locks halten.

Unter Windows ist dies der Typ SWRLOCK - und tatsächlich wird diese Sperre normalerweise zum Implementieren von Lese-/Schreibsperren verwendet. Viele Leser erlaubt, aber das Schreiben muss exklusiv sein.

Hier ist ein Beispielcode zum Erstellen von zwei Vorlagenwrappern für gemeinsam genutzte und nicht gemeinsam genutzte Mutexe. In einem Fall haben wir Lese- und Schreibvorgänge, die unterschiedliche Sperren erfordern. In der anderen haben wir nur Zugriff auf:

template<class T, class M=std::mutex>
struct mutex_guarded {
  template<class F>
  auto access( F&& f ) {
    auto l = lock();
    return std::forward<F>(f)(t);
  }
  template<class F>
  auto access( F&& f ) const {
    auto l = lock();
    return std::forward<F>(f)(t);
  }
  mutex_guarded(mutex_guarded const&)=delete;
  mutex_guarded& operator=(mutex_guarded const&)=delete;
  template<class...Ts>
  mutex_guarded( Ts&&...ts ):t(std::forward<Ts>(ts)...){}
  mutex_guarded()=default;
protected:
  mutable M m;
  T t;
  auto lock() { return std::unique_lock<M>(m); }
};
template<class T, class M=std::shared_mutex>
struct shared_mutex_guarded:private mutex_guarded<T, M> {
  using base = mutex_guarded<T, M>;
  template<class F>
  auto read( F&& f ) const { return access(std::forward<F>(f)); }
  template<class F>
  auto write( F&& f ) { return access(std::forward<F>(f)); }

  using base::base;
protected:
  using base::access;
  template<class F>
  auto access( F&& f ) const {
    auto l = lock();
    return std::forward<F>(f)(this->t);
  }
  using base::lock;
  auto lock() const { return std::shared_lock<M>(this->m); }
};