2016-07-18 11 views
3

Ich habe mich gefragt, was passiert, wenn Sie eine unique_lock verschieben, die eine recursive_mutex enthält.Einen unique_lock <rekursive_mutex> in einen anderen Thread verschieben

Insbesondere war ich an diesem Code suchen:

recursive_mutex g_mutex; 

#define TRACE(msg) trace(__FUNCTION__, msg) 

void trace(const char* function, const char* message) 
{ 
    cout << std::this_thread::get_id() << "\t" << function << "\t" << message << endl; 
} 

future<void> foo() 
{ 
    unique_lock<recursive_mutex> lock(g_mutex); 
    TRACE("Owns lock"); 
    auto f = std::async(launch::async, [lock = move(lock)]{ 
     TRACE("Entry"); 
     TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!"); // Prints Owns lock! 
     this_thread::sleep_for(chrono::seconds(3)); 
    }); 
    TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!"); // Prints Doesn't own lock! 
    return f; 
} 


int main() 
{ 
    unique_lock<recursive_mutex> lock(g_mutex); 
    TRACE("Owns lock"); 
    auto f = foo();  
    TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!");  // Prints Owns lock! 
    f.wait(); 
    TRACE(lock.owns_lock()? "Owns lock!" : "Doesn't own lock!");  // Prints Owns lock! 
} 

Die Ausgabe dieses Beispielcode hat mich sehr überrascht. Wie weiß die unique_lock in main(), dass der Thread den Mutex freigegeben hat? Ist es echt?

+3

Es ist nicht klar, was Sie überraschend finden. Es gibt ein einfaches boolesches Element in 'unique_lock', das' owns_lock() 'zurückgibt und das vom move-Konstruktor in einer vorhersagbaren und dokumentierten Weise verschoben wird. 'ownes_lock()' berührt den zugrunde liegenden Mutex nicht. Allerdings zeigt Ihr Programm undefiniertes Verhalten: Wenn 'unique_lock' im Worker-Thread zerstört wird, ruft es' g_mutex.unlock() 'auf, aber der Worker-Thread hält keine Sperre für' g_mutex' (was ein pre ist -Erfordernis für 'unlock()'). –

+0

@IgorTandetnik Danke. Es ist also nicht möglich, den Besitz eines 'rekursiven_Mutex' zwischen Threads zu verschieben. Was wäre, wenn der Mutex nicht rekursiv wäre? Würde das Verschieben des 'unique_lock' den Eigentümer wirklich in den Eigentümer-Thread verschieben? –

+3

Das Verschieben von 'unique_lock' zwischen Threads ist absolut nicht gut für Sie. Erkenne, dass "unique_lock" nichts anderes ist als ein "mutex *" - Zeiger und eine "bool owns" -Flagge - es gibt keine schwarze Magie. Der Verschiebungskonstruktor bewegt sich einfach über diesen Zeiger und diesen booleschen Wert. Der Aufruf von 'my_mutex.unlock()' an einem anderen Thread als dem, der 'my_mutex.lock()' aufgerufen hat, weist ein nicht definiertes Verhalten auf, egal ob explizit oder indirekt, indem 'unique_lock' dazu verleitet wird. Dies gilt für alle Mutex-Aromen. –

Antwort

3

Sie scheinen unique_lock einige magische Eigenschaften zuzuschreiben. Es hat keine, es ist eine sehr einfache Klasse. Es hat zwei Datenelemente, Mutex* pm und bool owns (Mitgliedsnamen nur für Exposition gezeigt). lock() ist einfach pm->lock(); owns = true;, und unlock tut pm->unlock(); owns = false;. Der Destruktor ist if (owns) unlock();. Verschiebe Konstruktorkopien über die beiden Elemente und setze sie im Original entsprechend auf nullptr und false. owns_lock() gibt den Wert owns member zurück.

Die gesamte Garnsynchronisierung Magie ist in der Mutex selbst, und seine lock() und unlock() Methoden. unique_lock ist nur eine dünne Hülle um ihn herum.

Nun muss der Thread, der mutex.unlock() aufruft, als Voraussetzung den Mutex (dh der Thread hat vorher lock() darauf) verwendet haben, da andernfalls das Programm undefiniertes Verhalten aufweist. Dies gilt unabhängig davon, ob Sie explizit unlock aufrufen oder einen Helfer wie unique_lock dazu bringen, ihn für Sie anzurufen.

In Anbetracht dessen ist das Verschieben einer unique_lock Instanz in einen anderen Thread lediglich ein Rezept zum Auslösen von undefiniertem Verhalten kurz danach; es gibt keinen Vorteil.

Verwandte Themen