webentwicklung-frage-antwort-db.com.de

Beispiel für einen Boost von shared_mutex (mehrere Lesevorgänge / ein Schreibvorgang)?

Ich habe eine Multithread-App, die einige Daten häufig lesen muss, und gelegentlich werden diese Daten aktualisiert. Im Moment bewahrt ein Mutex den Zugriff auf diese Daten sicher auf, aber es ist teuer, da ich möchte, dass mehrere Threads gleichzeitig lesen können und sie nur sperren, wenn ein Update erforderlich ist (der Update-Thread könnte warten, bis die anderen Threads fertig sind). .

Ich denke das ist was boost::shared_mutex sollte es tun, aber ich bin nicht sicher, wie ich es verwenden soll, und habe kein klares Beispiel gefunden.

Hat jemand ein einfaches Beispiel, mit dem ich anfangen könnte?

111
kevin42

Es sieht so aus, als würden Sie so etwas tun:

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
}
98

1800 INFORMATION ist mehr oder weniger korrekt, aber es gibt ein paar Probleme, die ich korrigieren wollte.

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
}

Beachten Sie auch, dass im Gegensatz zu einem shared_lock nur ein einzelner Thread gleichzeitig ein upgrade_lock erhalten kann, auch wenn es nicht aktualisiert wurde (was ich als unangenehm empfunden habe, als ich darauf gestoßen bin). Wenn alle Ihre Leser bedingte Schreiber sind, müssen Sie eine andere Lösung finden.

163
mmocny

Seit C++ 17 (VS2015) können Sie den Standard für Schreib-/Lesesperren verwenden:

#include <shared_mutex>

typedef std::shared_mutex Lock;
typedef std::unique_lock< Lock > WriteLock;
typedef std::shared_lock< Lock > ReadLock;

Lock myLock;


void ReadFunction()
{
    ReadLock r_lock(myLock);
    //Do reader stuff
}

void WriteFunction()
{
     WriteLock w_lock(myLock);
     //Do writer stuff
}

Für ältere Versionen können Sie boost mit derselben Syntax verwenden:

#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>

typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock >  WriteLock;
typedef boost::shared_lock< Lock >  ReadLock;
45
Yochai Timmer

Um weitere empirische Informationen hinzuzufügen, habe ich das gesamte Problem der aktualisierbaren Sperren untersucht und Beispiel für boost shared_mutex (mehrere Lesevorgänge/ein Schreibvorgang)? ist eine gute Antwort und fügt die wichtigen Informationen hinzu, die nur einer enthält Thread kann ein upgrade_lock haben, auch wenn es nicht aktualisiert wird. Dies ist wichtig, da Sie kein Upgrade von einem freigegebenen Schloss auf ein eindeutiges Schloss durchführen können, ohne das freigegebene Schloss zuvor aufzuheben. (Dies wurde an anderer Stelle diskutiert, aber der interessanteste Thread ist hier http://thread.gmane.org/gmane.comp.lib.boost.devel/214394 )

Allerdings habe ich einen wichtigen (undokumentierten) Unterschied zwischen einem Thread, der auf ein Upgrade auf eine Sperre wartet (dh darauf warten muss, dass alle Leser die gemeinsam genutzte Sperre aufheben) und einem Writer-Lock, der auf dasselbe wartet (dh eine unique_lock), festgestellt.

  1. Der Thread, der auf ein unique_lock im shared_mutex wartet, blockiert alle neuen Leser, die hereinkommen. Sie müssen auf die Schreibanforderung warten. Dies stellt sicher, dass Leser keine Schriftsteller hungern lassen (ich glaube jedoch, dass Schriftsteller Leser hungern lassen könnten).

  2. Der Thread, der auf ein Upgrade eines upgrade-fähigen_Locks wartet, ermöglicht es anderen Threads, eine gemeinsame Sperre zu erhalten, sodass dieser Thread bei sehr häufigen Lesern ausgehungert werden kann.

Dies ist ein wichtiges Thema, das zu berücksichtigen ist und wahrscheinlich dokumentiert werden sollte.

17
Jim Morris

Verwenden Sie ein Semaphor mit einer Anzahl, die der Anzahl der Leser entspricht. Lassen Sie jeden Leser eine Zählung des Semaphors durchführen, damit alle gleichzeitig lesen können. Lassen Sie den Schreiber dann vor dem Schreiben ALLE Semaphorenzählungen durchführen. Dadurch wartet der Writer, bis alle Lesevorgänge abgeschlossen sind, und blockiert dann die Lesevorgänge beim Schreiben.

2
R Virzi

Großartige Antwort von Jim Morris, ich bin darauf gestoßen und es hat eine Weile gedauert, bis ich herausgefunden habe. Hier ist ein einfacher Code, der zeigt, dass nach dem Senden einer "Anfrage" für einen unique_lock-Boost (Version 1.54) alle shared_lock-Anfragen blockiert werden. Dies ist sehr interessant, da es meiner Meinung nach möglich ist, zwischen unique_lock und upgradeable_lock zu wählen, ob Schreibpriorität oder keine Priorität gewünscht wird.

Auch (1) in Jim Morris 'Beitrag scheint dem zu widersprechen: Boost shared_lock. Bevorzugt lesen?

#include <iostream>
#include <boost/thread.hpp>

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();
}
2
dale1209