Aufruf von pthread_cond_signal ohne Sperren des Mutex

81

Ich habe irgendwo gelesen, dass wir den Mutex sperren sollten, bevor wir pthread_cond_signal aufrufen, und den Mutext entsperren sollten, nachdem wir ihn aufgerufen haben:

Die Routine pthread_cond_signal () wird verwendet, um einen anderen Thread zu signalisieren (oder aufzuwecken), der auf die Bedingungsvariable wartet. Es sollte aufgerufen werden, nachdem der Mutex gesperrt wurde, und muss den Mutex entsperren, damit die Routine pthread_cond_wait () abgeschlossen wird.

Meine Frage ist: Ist es nicht in Ordnung, die Methoden pthread_cond_signal oder pthread_cond_broadcast aufzurufen , ohne den Mutex zu sperren?

B Faley
quelle

Antworten:

139

Wenn Sie den Mutex nicht im Codepfad sperren, der den Zustand und die Signale ändert, können Sie das Aufwecken verlieren. Betrachten Sie dieses Prozesspaar:

Prozess A:

pthread_mutex_lock(&mutex);
while (condition == FALSE)
    pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);

Prozess B (falsch):

condition = TRUE;
pthread_cond_signal(&cond);

Betrachten Sie dann diese mögliche Verschachtelung von Anweisungen, wobei conditionFolgendes beginnt FALSE:

Process A                             Process B

pthread_mutex_lock(&mutex);
while (condition == FALSE)

                                      condition = TRUE;
                                      pthread_cond_signal(&cond);

pthread_cond_wait(&cond, &mutex);

Das conditionist jetzt TRUE, aber Prozess A wartet nicht mehr auf die Bedingungsvariable - er hat das Wecksignal verpasst. Wenn wir Prozess B ändern, um den Mutex zu sperren:

Prozess B (richtig):

pthread_mutex_lock(&mutex);
condition = TRUE;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);

... dann kann das oben genannte nicht auftreten; Das Aufwachen wird nie fehlen.

(Beachten Sie, dass Sie können tatsächlich die bewegen pthread_cond_signal()sich nach dem pthread_mutex_unlock(), aber in weniger optimalen Planung von Threads zur Folge haben kann, und Sie haben notwendigerweise den Mutex gesperrt bereits in diesem Codepfad aufgrund Änderung der Bedingung selbst).

caf
quelle
3
@Nemo: Ja, im "richtigen" Pfad pthread_signal_cond() kann der nach dem Mutex-Unlock verschoben werden, obwohl es wahrscheinlich besser ist, dies nicht zu tun . Es ist vielleicht richtiger zu sagen, dass Sie an dem Punkt, an dem Sie anrufen pthread_signal_cond(), den Mutex bereits gesperrt haben müssen, um die Bedingung selbst zu ändern.
Café
@Nemo: Ja, der Code ist defekt, weshalb er als "falsch" markiert ist. Ich habe die Erfahrung gemacht, dass Personen, die diese Frage stellen, häufig in Betracht ziehen, die Sperrung vollständig auf dem Signalweg zu belassen. Vielleicht unterscheidet sich Ihre Erfahrung.
Café
8
Für das, was es wert ist, scheint die Frage, ob vor oder nach dem Signal entsperrt werden soll, nicht trivial zu beantworten. Durch das Entsperren nach wird sichergestellt, dass ein Thread mit niedrigerer Priorität das Ereignis nicht von einem Thread mit höherer Priorität stehlen kann. Wenn Sie jedoch keine Prioritäten verwenden, wird durch das Entsperren vor dem Signal die Anzahl der Systemaufrufe / Kontextwechsel und verringert Verbesserung der Gesamtleistung.
R .. GitHub STOP HELPING ICE
1
@R .. Du hast es rückwärts. Das Entsperren vor dem Signal erhöht die Anzahl der Systemaufrufe und verringert die Gesamtleistung. Wenn Sie vor dem Entsperren signalisieren, weiß die Implementierung, dass das Signal einen Thread möglicherweise nicht wecken kann (da jeder im Lebenslauf blockierte Thread den Mutex benötigt, um Fortschritte zu erzielen), sodass er den schnellen Pfad verwenden kann. Wenn Sie nach dem Entsperren signalisieren, können sowohl das Entsperren als auch das Signal Threads aktivieren, was bedeutet, dass beide teure Vorgänge sind.
David Schwartz
1
@Alceste_: Siehe diesen Text in POSIX : "Wenn ein Thread auf eine Bedingungsvariable wartet, nachdem er einen bestimmten Mutex für die pthread_cond_timedwait()oder die pthread_cond_wait()Operation angegeben hat, wird eine dynamische Bindung zwischen diesem Mutex und der Bedingungsvariablen gebildet, die so lange wie gültig bleibt Mindestens ein Thread ist für die Bedingungsvariable blockiert. Während dieser Zeit ist die Auswirkung eines Versuchs eines Threads, mit einem anderen Mutex auf diese Bedingungsvariable zu warten, undefiniert. "
caf
48

Nach diesem Handbuch:

Die Funktionen pthread_cond_broadcast()oder können von einem Thread aufgerufen werden, unabhängig davon, ob er derzeit den Mutex besitzt, den Threads während ihrer Wartezeit aufrufen oder der Bedingungsvariablen zugeordnet haben. Wenn jedoch ein vorhersehbares Planungsverhalten erforderlich ist, muss dieser Mutex durch den Thread, der oder aufruft, gesperrt werden .pthread_cond_signal()pthread_cond_wait()pthread_cond_timedwait()pthread_cond_broadcast()pthread_cond_signal()

Die Bedeutung der vorhersagbaren Planungsverhaltensangabe wurde von Dave Butenhof (Autor von Programming with POSIX Threads ) auf comp.programming.threads erläutert und ist hier verfügbar .

Eiskriminalität
quelle
6
+1 für die Verknüpfung der Mail von Dave Butenhof. Ich habe mich immer selbst über dieses Problem gewundert, jetzt weiß ich ... Ich habe heute etwas Wichtiges gelernt. Vielen Dank.
Nils Pipenbrinck
Wenn ein vorhersehbares Planungsverhalten erforderlich ist, platzieren Sie die Anweisungen in der gewünschten Reihenfolge in einem Thread oder verwenden Sie Thread-Prioritäten.
Kaz
7

caf, in Ihrem Beispielcode ändert sich Prozess B, conditionohne zuerst den Mutex zu sperren. Wenn Prozess B den Mutex während dieser Änderung einfach gesperrt und dann den Mutex vor dem Aufruf immer noch entsperrt hat pthread_cond_signal, gibt es kein Problem - habe ich Recht damit?

Ich glaube intuitiv, dass die Position des Cafés korrekt ist: Anrufen pthread_cond_signalohne das Mutex-Schloss zu besitzen, ist eine schlechte Idee. Aber das Beispiel des Cafés ist jedoch kein Beweis für diese Position. Es ist lediglich ein Beweis für die viel schwächere (praktisch selbstverständliche) Position, dass es eine schlechte Idee ist, den durch einen Mutex geschützten gemeinsamen Status zu ändern, es sei denn, Sie haben diesen Mutex zuerst gesperrt.

Kann jemand einen Beispielcode bereitstellen, in dem ein Aufruf pthread_cond_signalgefolgt von einem pthread_mutex_unlockkorrekten Verhalten, ein Aufruf pthread_mutex_unlockgefolgt von einem pthread_cond_signalfalschen Verhalten jedoch ein korrektes Verhalten ergibt?

Quuxpluson
quelle
3
Eigentlich denke ich, dass meine Frage ein Duplikat dieser Frage ist , und die Antwort lautet: "Es ist in Ordnung, Sie können pthread_cond_signal vollständig aufrufen, ohne die Mutex-Sperre zu besitzen. Es gibt kein Problem mit der Korrektheit. Bei gängigen Implementierungen wird Ihnen jedoch eine clevere Optimierung entgehen." tief in pthreads, daher ist es etwas vorzuziehen, pthread_cond_signal aufzurufen, während Sie die Sperre gedrückt halten. "
Quuxplusone
Ich habe diese Bemerkung im letzten Absatz meiner Antwort gemacht.
Café
Hier haben Sie ein gutes Szenario: stackoverflow.com/a/6419626/1284631 Beachten Sie, dass nicht behauptet wird, dass das Verhalten falsch ist, sondern nur ein Fall, in dem das Verhalten möglicherweise nicht wie erwartet ist.
user1284631
Es ist möglich, Beispielcode zu erstellen, bei dem das Aufrufen pthread_cond_signalnachher pthread_mutex_unlockzu einem verlorenen Aufwecken führen kann, da das Signal als "falscher" Thread abgefangen wird, der blockiert wurde, nachdem die Änderung im Prädikat festgestellt wurde. Dies ist nur dann ein Problem, wenn dieselbe Bedingungsvariable für mehr als ein Prädikat verwendet werden kann und Sie sie nicht verwenden. Dies ist pthread_cond_broadcastohnehin ein seltenes und fragiles Muster.
David Schwartz