5

Ich möchte nach dem einfachsten Mutex-Ansatz für Multi-Threading fragen. Ist der folgende Code threadsicher (quick-n-dirty)?Einfachster Mutex aller Zeiten. Funktioniert dieses Beispiel? Ist es threadsicher?

class myclass 
{ 
    bool locked; 
    vector<double> vals; 

    myclass(); 
    void add(double val); 
}; 

void myclass::add(double val) 
{ 
    if(!locked) 
    { 
     this->locked = 1; 
     this->vals.push_back(val); 
     this->locked = 0; 
    } 
    else 
    { 
     this->add(val); 
    } 
} 

int main() 
{ 
    myclass cls; 
    //start parallelism 
    cls.add(static_cast<double>(rand())); 
} 

Funktioniert das? Ist es threadsicher? Ich versuche nur zu verstehen, wie der einfachste Mutex geschrieben werden kann.

Wenn Sie einen Rat zu meinem Beispiel haben, wäre nett.

Vielen Dank.

Danke für die Aussage, dass es nicht funktioniert. Können Sie bitte eine Korrektur vorschlagen, die vom Compiler unabhängig ist?

+5

Einen korrekten Mutex-Mechanismus zu erstellen ist ein komplexes Geschäft. Ich würde etwas schon da draußen benutzen. – andre

+0

Im Allgemeinen erfordern Synchronisationsgrundelemente wie Mutexe eine gewisse Atomizität (oft auf CPU-Befehlsebene). Hier ist ein interessanter Link, den ich gefunden habe: http://lists.canonical.org/pipermail/kragen-tol/1999-August/000457.html – Tom

+0

Der einfachste Mutex (oder Synchronisationskonstrukt im Allgemeinen) ist der, den Sie nicht brauchen; -) – delnan

Antwort

8

Ist es threadsicher?

Sicher nicht. Wenn ein Thread zwischen dem Überprüfen und dem Setzen der Sperre verhindert wird, könnte ein zweiter Thread diese Sperre erlangen; Wenn die Steuerung dann zum ersten Thread zurückkehrt, dann werden beide es erwerben. (Und natürlich könnten auf einem modernen Prozessor zwei oder mehr Kerne die gleichen Befehle gleichzeitig für noch mehr Heiterkeit ausführen.)

Zumindest benötigen Sie eine atomare Test-and-Set-Operation, um eine Sperre zu implementieren so was. Die C++ 11-Bibliothek bietet so eine Sache:

std::atomic_flag locked; 

if (!locked.test_and_set()) { 
    vals.push_back(val); 
    locked.clear(); 
} else { 
    // I don't know exactly what to do here; 
    // but recursively calling add() is a very bad idea. 
} 

oder besser noch:

std::mutex mutex; 

std::lock_guard<std::mutex> lock(mutex); 
vals.push_back(val); 

Wenn Sie eine ältere Implementierung haben, dann werden Sie gleich aus welchen Erweiterungen angewiesen sind/Bibliotheken verfügbar sind für dich, da es damals in der Sprache oder der Standardbibliothek nichts Hilfreiches gab.

+0

hmmmmm. Danke für die Antworten. Ich verwende VS2010 und g ++, wobei der erste Mutex nicht unterstützt. Irgendeine andere Compiler-unabhängige Idee? –

+1

@SamerAfach: Die [Boost.Thread] (http://www.boost.org/doc/libs/release/doc/html/thread.html) und [just :: thread] (http: //www.stdthread .co.uk /) Bibliotheken haben portable und (mehr oder weniger) direkte Ersetzungen für den C++ 11 Mutex. Andernfalls gibt es die Windows-API (kritische Abschnitte oder wie sie es nennen); offensichtlich ist das nicht Compiler-unabhängig, also benötigen Sie einige sorgfältig platzierte '# define's, um auszuwählen, welche verwendet werden sollen. –

+0

@MikeSymour Wenn ich zum Beispiel den Boost-Mutex (oder irgendeinen anderen Mutex) verwende, wird das den Code für andere Parallelisierungsbibliotheken wie OpenMPI und OpemMP threadsicher machen? oder arbeitet Mutex ausschließlich für die Parallelität der Bibliothek, aus der der Mutex stammt? –

2

Funktioniert das? Ist es threadsicher?

Nein. Es wird manchmal fehlschlagen.

Ihre Mutex wird nur funktionieren, wenn andere Threads nie etwas zwischen der Ausführung dieser beiden Linien tun:

if(!locked) 
{ 
    this->locked = 1; 

... und Sie haben, dass nicht gewährleistet.

Um mehr über die wie des Mutex schreiben zu erfahren, siehe this SO post.

2

Nein, das ist nicht threadsicher.

Betrachten Sie zwei Threads laufen myclass::add mehr oder weniger zur gleichen Zeit. Stellen Sie sich auch vor, dass der Wert .lockedfalse ist.

Der erste Thread führt bis zu und einschließlich dieser Linie:

if(!locked) 
{ 

sich nun vor, dass der Systemkontext mit dem zweiten Thread umschaltet. Es wird auch in derselben Zeile ausgeführt.

Jetzt haben wir zwei verschiedene Threads, beide glauben, dass sie exklusiven Zugriff haben, und beide innerhalb der !locked Zustand des if.

Sie werden beide vals.push_back() mehr oder weniger zur gleichen Zeit aufrufen.

Boom.

5

Nein, das ist nicht threadsicher. Es ist ein Rennen zwischen

if(!locked) 

und

this->locked = 1; 

Wenn es einen Kontextwechsel zwischen diesen beiden Aussagen ist, Ihr Verriegelungsmechanismus fällt auseinander. Sie benötigen eine atomare test and set Anweisung, oder verwenden Sie einfach eine vorhandene mutex.

5

Dieser Code bietet keine atomare Modifikation von vals Vektor. Betrachten Sie das folgende Szenario:

//<<< Suppose it's 0 
if(!locked) 
{ //<<< Thread 0 passes the check 
    //<<< Context Switch - and Thread 1 is also there because locked is 0 
    this->locked = 1; 
    //<<< Now it's possible for one thread to be scheduled when another one is in 
    //<<< the middle of modification of the vector 
    this->vals.push_back(val); 
    this->locked = 0; 
} 
1

Andere haben bereits gezeigt, wie Ihr Mutex fehlschlagen kann, so dass ich nicht ihre Punkte aufwärmen. Ich werde nur eine Sache hinzufügen: Die einfachste Mutex-Implementierung ist viel komplizierter als Ihr Code.

Wenn Sie in der Nitty Gritty interessiert sind (oder auch wenn Sie nicht - das ist Zeug jeder Software-Entwickler wissen sollten) Sie von dort in Leslie Lamport's Bakery Algorithm und gehen aussehen sollte.