2009-08-02 6 views
6

Wenn ein Mutex innerhalb einer Funktion definiert ist, gilt seine Sperre für Funktionen, die von dieser Funktion aufgerufen werden? dhGilt eine Sperre für einen Mutex auch für aufgerufene Funktionen?

void f() { 
    Mutex mutex; 
    g(); 
} 

Gilt die Sperre noch für alle Datenänderungen in g()?

Kann ich auch sagen, dass eine in einer Klassenmethode definierte Sperre nur für bestimmte Instanzen dieser Klasse gilt? Bedeutung:

Class Foo; 
Foo foo1, foo2; 
(In thread 1) foo1.bar(); 
(In thread 2) foo2.bar(); 

Wäre jeder Aufruf gleichzeitig möglich?

Es wäre ein netter Bonus, wenn jemand Links erklären/erklären könnte, die den Mechanismus hinter Mutexen erklären. Vielen Dank! Ich arbeite derzeit mit der Qt Thread-Bibliothek, wenn diese Informationen helfen.

Antwort

15

In Ihrem Beispiel sperren Sie den Mutex nicht, sodass verschiedene Threads nicht gleichzeitig auf die Funktion zugreifen können. Außerdem deklarieren Sie den Mutex lokal in der Funktion, so dass jeder Funktionsaufruf ein anderes lokales Mutex-Objekt verwendet. Selbst wenn dieser Mutex gesperrt wäre, würde jeder Funktionsaufruf ein anderes Mutex-Objekt sperren und den gleichzeitigen Zugriff nicht verhindern.

Eine bessere Strategie eine Einrichtung wie diese wäre:

class A { 
    QMutex mutex; 

    void f() { 
    QMutexLocker ml(mutex); // Acquire a lock on mutex 
    g(); 

    // The lock on the mutex will be released when ml is destroyed. 
    // This happens at the end of this function. 
    } 

    // ... 
}; 

In diesem Fall mutex wird so lange gesperrt, wie ml vorhanden ist, so auch während der Zeit der Faden innerhalb g() verbringt. Wenn ein anderer Thread während dieser Zeit f() aufrufen würde, würde er bei der Erstellung seines Objekts blockieren, bis der erste Thread die Funktion verlassen hat und der neue Thread die Sperre auf mutex erhalten kann.

+1

+1 mit QT api –

7

Ein Mutex ist etwas, das Sie greifen, und stoppt alle anderen Threads, die versuchen, es zu greifen, bis Sie es aus dem Grabbing-Thread freigeben.

In Ihrer Frage haben Sie eine Funktion zum Zuweisen einer Mutex-Instanz. Das ist nicht genug, um es zu sperren. Du musst mutex.lock() aufrufen (in Qt, aber auch generell, außer du verwendest pthread, in diesem Fall benutze pthread_mutex_lock und hab Spaß mit plattformabhängigen Sachen auf niedriger Ebene. Qt abstrahiert es sehr gut).

hier ein Beispiel mit Qt

void MyClass::doStuff(int c) 
    { 
     mutex.lock(); 
     a = c; 
     b = c * 2; 
     mutex.unlock(); 
    } 

Sobald Sie die Sperre, um den Anruf zu g() erhalten wird aus dem Thread, der die Sperre bekam getan werden, so dass es in diesem Anruf allein seine unter der Annahme,, dass Sie g() nicht von anderen Threads aus einem anderen Teil des Codes aufrufen. Sperren bedeutet nicht, dass alle anderen Threads gestoppt werden. Es stoppt Threads, die versuchen, dieselbe Sperre zu erhalten, bis die Sperre aufgehoben wird.

Wenn dies der einzige Weg für Ihre Threads ist, g() zu erreichen, dann werden Sie auf diesen Zugriff synchronisiert.

Für den zweiten Teil Ihrer Frage, wenn der Mutex ein Instanzattribut ist, dann werden sie zwei verschiedene Mutexe sein. Sie müssen eine Klassen-Mutex-Instanz deklarieren und instanziieren und sich auf Ihre Sperrung beziehen. In diesem Fall wird jeder Versuch, eine Methode in der Klasse aufzurufen, die den Klassenmutex sperrt, effektiv synchronisiert, was bedeutet, dass keine zwei Threads diese Methode zusammen ausführen.

Zum Beispiel (ich weiß nicht Qt habe, so kann ich diesen Code nicht kompilieren, und ich hörte vor mit ihm 2 Jahren Codierung, so könnte es nicht funktionieren)

class Foo { 
public: 
    void method(void) { 
     mutex.lock(); 
     cout << "method called"; 
     // long computation 
     mutex.unlock(); 
    } 

private: 
    QMutex mutex; 
}; 

Ok, in diesem Fall, Angenommen, Sie haben zwei Threads, 1 und 2, und zwei Instanzen der Klasse Foo, a und b. Angenommen, Thread 1 ruft a.method() auf und Thread 2 ruft b.method() auf. In diesem Fall sind die beiden Mutexe unterschiedliche Instanzen, sodass jeder Thread den Aufruf unabhängig ausführt und parallel abläuft.

Angenommen, Sie haben zwei Threads, 1 und 2, und eine Instanz der Klasse Foo, die zwischen den beiden Threads geteilt wird. Wenn Thread 1 a.method() aufruft und Thread 2 a.method() aufruft, wird Thread 2 anhalten und warten, bis die Mutex-Sperre aufgehoben wird.

Schließlich

class Foo { 
public: 
    void method(void) { 
     mutex.lock(); 
     cout << "method called"; 
     // long computation 
     mutex.unlock(); 
    } 

private: 
    static QMutex mutex; 
}; 

QMutex Foo::mutex; 

In diesem Fall ist der Mutex eine Klasse statische Variable. Sie haben nur eine Instanz des Mutex für jede Objektinstanz. Angenommen, Sie hatten dieselbe Situation wie im ersten Fall: zwei Threads und zwei Instanzen. Wenn in diesem Fall der zweite Thread versucht, b.method() aufzurufen, muss er darauf warten, dass a.method() vom ersten Thread abgeschlossen wird, da die Sperre jetzt eindeutig ist und von allen Instanzen Ihrer Klasse gemeinsam genutzt wird.

Für mehr Informationen, hat Qt ein nettes Tutorial auf Multithreading

https://doc.qt.io/qt-5/threads.html

+0

Entschuldigung, ich habe einen wirklich schlechten Versuch bei Pseudocode gemacht. Habe es einfach repariert. Ihre Antwort für den ersten Teil war genau das, worüber ich mich wunderte, danke! – int3

2

Ihre Mutex instatiated lokal, auf dem Stapel. So wird ein Aufruf von f() von einem Thread seine eigene Instanz des Mutex sperren. Jeder andere Aufruf von f() von einem anderen Thread wird seinen eigenen sperren. Es könnte also eine Wettlaufsituation mit Daten auftreten, auf die von g() zugegriffen wird! Auch schwierig nennen Sie es auf der gleichen Klasseninstanz:

MyClass foo; 
(In thread 1) foo->f(); 
(In thread 2) foo->f(); 

Wie man besser mit Sperre umgehen hängt davon ab, was Sie tun möchten. Nach dem, was Sie gesagt haben, wäre eine bessere Richtlinie die g() Implementierung direkt zu modifizieren: Sie muss einen Mutex zum Beispiel als global deklarieren oder als statisch in g() für jeden Aufruf von g() freigeben. Solange ich verstehe, möchten Sie Ihre Daten global sperren?

+0

Wenn ich also Daten habe, die ich in bestimmten Instanzen von Foo :: f() sperren will, sollte ich den Mutex als nicht-statische Klassendaten instanziieren? – int3

+0

Nun, ich denke, es sollte den Trick tun. Jeglicher Aufruf von der 'foo' -Instanz von irgendeinem Thread in meinem Beispiel verhindert den gleichzeitigen Zugriff. (Aber nicht von 2 verschiedenen Instanzen natürlich) –

Verwandte Themen