2016-04-21 8 views
0

Betrachten Sie das nächste Stück Code -seltsam pthread_mutex_t Verhalten

#include <iostream> 

using namespace std; 

int sharedIndex = 10; 
pthread_mutex_t mutex; 

void* foo(void* arg) 
{ 
    while(sharedIndex >= 0) 
    { 
     pthread_mutex_lock(&mutex); 

     cout << sharedIndex << endl; 
     sharedIndex--; 

     pthread_mutex_unlock(&mutex); 
    } 

    return NULL; 
} 

int main() { 

    pthread_t p1; 
    pthread_t p2; 
    pthread_t p3; 

    pthread_create(&p1, NULL, foo, NULL); 
    pthread_create(&p2, NULL, foo, NULL); 
    pthread_create(&p3, NULL, foo, NULL); 

    pthread_join(p1, NULL); 
    pthread_join(p2, NULL); 
    pthread_join(p3, NULL); 

    return 0; 
} 

ich einfach drei erstellt pthreads und gab ihnen alle die gleiche Funktion foo, in der Hoffnung, dass jeder Faden, um seinerseits druckt und verringert die sharedIndex .

Aber das ist der Ausgang -

10 
9 
8 
7 
6 
5 
4 
3 
2 
1 
0 
-1 
-2 
  • Ich verstehe nicht, warum der Prozess stoppt nicht, wenn sharedIndex 0 erreicht
  • sharedIndex durch eine mutex geschützt ist. Wie kommt es, nachdem es 0 wurde? Müssen die Threads nicht direkt zu return NULL; springen?

EDIT

Darüber hinaus scheint es, dass nur der erste Thread die sharedIndex dekrementiert. Warum verringert nicht jeder Thread die freigegebene Ressource, wenn sie an der Reihe ist? Hier ist der Ausgang nach einem fix -

Current thread: 140594495477504 
10 
Current thread: 140594495477504 
9 
Current thread: 140594495477504 
8 
Current thread: 140594495477504 
7 
Current thread: 140594495477504 
6 
Current thread: 140594495477504 
5 
Current thread: 140594495477504 
4 
Current thread: 140594495477504 
3 
Current thread: 140594495477504 
2 
Current thread: 140594495477504 
1 
Current thread: 140594495477504 
0 
Current thread: 140594495477504 
Current thread: 140594478692096 
Current thread: 140594487084800 

Ich wünsche, dass alle von dem Thread werden die gemeinsame Quelle verringern - das heißt, jeder contex Schalter, ein anderer Thread auf die Ressource zugreifen und nicht sein Ding.

+2

versuchen Sie, 'pthread_mutex_init' vor dem Erstellen von Threads aufzurufen und' pthread_mutex_destroy' am Ende des Programms – mausik

+0

Warum versuchen Sie nicht, das undefinierte Verhalten in Ihrem Code zu beheben und sehen, ob es hilft? –

+0

Ich habe den Code geändert und es hat funktioniert. Aber immer noch - nur ein einziger Thread dekrementiert die Ressource –

Antwort

4

Th Das Verhalten des Programms ist nicht definiert.

Sie haben den Mutex nicht initialisiert. Sie müssen entweder pthread_mutex_init aufrufen oder es statisch initialisieren:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 

Sie lesen diese Variable außerhalb des kritischen Abschnitt:

while(sharedIndex >= 0) 

Das heißt, Sie einen Müll Wert lesen konnte, während ein anderer Thread es aktualisiert. Sie sollten die gemeinsam genutzte Variable erst lesen, wenn Sie den Mutex gesperrt haben und exklusiven Zugriff darauf haben.

Edit:

es scheint, dass nur der erste Thread die sharedIndex

dekrementiert, dass wegen der undefinierten Verhalten ist. Beheben Sie die oben genannten Probleme und Sie sollten sehen, dass andere Threads ausgeführt werden. Der Compiler darf mit Ihrem aktuellen Code davon ausgehen, dass der sharedIndex niemals von anderen Threads aktualisiert wird, so dass er es nicht wieder liest, sondern nur den ersten Thread zehn mal laufen lässt, dann die anderen zwei Threads einmal ausführen.

Bedeutung, jeder contex-Schalter, ein anderer Thread wird auf die Ressource zugreifen und ihre Sache tun.

Es gibt keine Garantie, dass Pthread-Mutexe sich fair verhalten. Wenn Sie ein Round-Robin-Verhalten garantieren möchten, bei dem jeder Thread der Reihe nach ausgeführt wird, müssen Sie dies selbst erzwingen, z. indem Sie eine andere Variable (und möglicherweise eine Bedingungsvariable) haben, die angibt, welcher Thread an der Reihe ist, und die anderen Threads blockiert, bis sie an der Reihe sind.

3

Die Fäden hängen an pthread_mutex_lock(&mutex); hängen, warten auf das Schloss. Sobald ein Thread auf 0 dekrementiert und die Sperre aufhebt, geht der nächste Thread, der auf Sperre wartet, dann um seine Aufgabe (macht den Wert -1), und dasselbe für den nächsten Thread (macht den Wert -2).

Sie müssen Ihre Logik ändern, um den Wert zu überprüfen und den Mutex zu sperren.

3
int sharedIndex = 10; 
pthread_mutex_t mutex; 

void* foo(void* arg) 
{ 
    while(sharedIndex >= 0) 
    { 
     pthread_mutex_lock(&mutex); 

     cout << sharedIndex << endl; 
     sharedIndex--; 

     pthread_mutex_unlock(&mutex); 
    } 

    return NULL; 
} 

Gemäß diesem Code ist die sharedIndexgemeinsam genutzte Ressource für alle Threads.

So sollte jeder Zugriff (sowohl Lesen und Schreiben) von Mutex umschlossen werden. Ansonsten nehmen Sie die Situation an, in der alle Threads sharedIndex gleichzeitig simulieren und der Wert 1 ist.

Alle Threads, dann, geben Sie die while Schleife und jeder abnimmt sharedIndex von einem führenden -2 am Ende.

EDIT

Mögliche fix (als eine der möglichen Optionen):

bool is_positive; 
do 
{ 
    pthread_mutex_lock(&mutex); 

    is_positive = (sharedIndex >= 0); 
    if (is_positive) 
    { 
     cout << sharedIndex << endl; 
     sharedIndex--; 
    } 

    pthread_mutex_unlock(&mutex); 
}while(is_positive); 

EDIT2

Beachten Sie, dass Sie den Mutex initialisieren müssen:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
+1

Große Antwort bis zum letzten Absatz, der einen Downvote verdient hat."volatile" ist hier nicht sinnvoll, der Compiler verschiebt niemals Zugriffe auf die Variable außerhalb der Mutex-Sperren (es sei denn, der Compiler ist völlig kaputt), so dass es keine Rolle spielt, ob ein Register verwendet wird, solange das Ergebnis endet Speicher am Ende des kritischen Abschnitts. –

+0

Danke, Ihre mögliche Lösung hat den Trick gemacht. –

+0

@ JonathanWakely Der Compiler kann (wahrscheinlich) 'sharedIndex' zu Beginn der Funktion in' eax' versetzen und vor 'return' zurück in' sharedIndex' speichern. Vielleicht wird es das nicht tun, aber ich weiß nicht, ob hier irgendein "Gesetz" angewendet werden kann, das den Compiler davon abhält, es zu tun. Wenn der Compiler die Implementierung von 'pthread_mutex_lock' und' pthread_mutex_unlock' kennt, kann es dazu kommen, dass nichts bewirkt, dass 'sharedIndex' und' sharedIndex' diese Funktionen nicht beeinflussen. Eine solche Optimierung ist also möglich. –