Bedingungsvariable – warum ist der Aufruf von pthread_cond_signal() vor dem Aufruf von pthread_cond_wait() ein logischer Fehler?

Lesezeit: 7 Minuten

Bedingungsvariable warum ist der Aufruf von pthread cond signal vor dem
Ascher Saban

Es ist im POSIX-Threads-Tutorial geschrieben
https://computing.llnl.gov/tutorials/pthreads/
dass es sich um einen logischen Fehler handelt.

Meine Frage ist, warum es ein logischer Fehler ist?

In meinem Programm muss ich diese Signale verwenden, aber ich kann nicht garantieren, dass es einen Thread gibt, der sich im Zustand _cond_wait befindet. Ich habe versucht es zu testen und es passiert nichts. Kann dies zu unerwartetem Verhalten oder Schlimmerem führen?

Danke schön!

Bedingungsvariable warum ist der Aufruf von pthread cond signal vor dem
stefaanv

Die Antwort von blaze kommt dem am nächsten, ist aber nicht ganz eindeutig:
Bedingte Variablen sollten nur verwendet werden, um eine Änderung einer Bedingung zu signalisieren.

Thread 1 prüft eine Bedingung. Wenn die Bedingung nicht erfüllt ist, wartet er auf die Bedingungsvariable, bis die Bedingung erfüllt ist. Da die Bedingung zuerst geprüft wird, sollte es ihm egal sein, ob die Bedingungsvariable signalisiert wurde:

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

Thread 2 ändert die Bedingung und signalisiert die Änderung über die Bedingungsvariable. Es ist ihm egal, ob Threads warten oder nicht:

pthread_mutex_lock(&mutex); 
changeCondition(); 
pthread_mutex_unlock(&mutex); 
pthread_cond_signal(&cond)

Das Endergebnis ist: die Kommunikation erfolgt über eine Bedingung. Eine Bedingungsvariable weckt nur wartende Threads auf, damit sie die Bedingung überprüfen können.

Beispiele für Bedingungen:

  • Warteschlange ist nicht leer, daher kann ein Eintrag aus der Warteschlange genommen werden
  • Ein boolesches Flag wird gesetzt, sodass der Thread s wartet, bis der andere Thread signalisiert, dass es in Ordnung ist, fortzufahren
  • Einige Bits in einem Bitset sind gesetzt, sodass der wartende Thread die entsprechenden Ereignisse verarbeiten kann

siehe auch pthread-Beispiel

  • Im zweiten Code sollte pthread_cond_signal(&cond) meiner Meinung nach zwischen den Mutex-Sperren stehen

    – Herzog

    25. September 13 um 0:09 Uhr

  • @Duke: nein, sollte es nicht. Früher dachte ich das auch, aber es gibt keinen Grund, warum es so sein sollte, und wenn die empfangende Seite dieselbe oder eine höhere Priorität hat, haben Sie 2 zusätzliche Kontextwechsel, da die empfangende Seite blockieren muss, bis der Sender den Mutex entsperrt.

    – stefaanv

    25. September 13 um 08:32 Uhr

  • @Konstantin Danke, wohlgemerkt, dass modernere Konstrukte (zB C++11-Threading) helfen Ihnen bei der Verwendung von Bedingungsvariablen auf diese Weise.

    – stefaanv

    29. April 15 um 15:14 Uhr

  • @LuisAverhoff: Thread 1 wird fast immer im sein pthread_cond_wait() aufrufen, wo der Mutex entsperrt ist. Deshalb muss diesem Aufruf der Mutex übergeben werden. Thread 2 muss also nur warten, wenn Thread 1 die Bedingung tatsächlich überprüft. Dafür ist der Mutex da: die Ressourcen zu schützen, aus denen die Bedingung besteht.

    – stefaanv

    19. Oktober 16 um 15:24 Uhr

  • Danke, das erklärt das Verhalten, das ich in meiner App debuggte: my startThread() Die Funktion wurde sofort beendet, als der Thread gestartet wurde, war aber noch nicht eingetreten pthread_cond_wait. Wenn ich anrief stopThread() sofort und drinnen habe ich gesetzt predicate=true; pthread_cond_signal(), dann ist mein Thread nicht aufgewacht, weil das Signal zu früh kam. Jetzt weiß ich, dass es beabsichtigt ist, und ich sollte cond_signal nicht setzen, wenn ich nicht sicher bin, ob ein Thread darauf wartet. Eine Problemumgehung war einfach – überprüfen Sie das Prädikat einmal in der Thread-Schleife, bevor Sie es betreten pthread_cond_wait.

    – JustAMartin

    1. Februar 17 um 9:50 Uhr


Meine 2 Cent: Ich kenne die Nebenwirkungen des Aufrufs von *pthread_cond_signal()* nicht, wenn kein Thread mit dem Aufruf von *pthread_cond_wait()* blockiert wurde. Dies ist wirklich ein Implementierungsdetail. Ich denke, wenn Ihr Threading-/Timing-Modell nicht die richtige Reihenfolge zwischen Warten und Signal garantiert, sollten Sie wahrscheinlich einen anderen Synchronisierungsmechanismus in Betracht ziehen [like a simple semaphore, for example] wenn Sie die Semaphore von Thread B signalisieren können, auch wenn Thread A den Synchronisationspunkt noch nicht erreicht hat. Wenn Thread A den Synchronisationspunkt erreicht, findet er das inkrementierte Semaphor vor und tritt in die kritische Sitzung ein.

  • +1 für den Vorschlag von Semaphoren, die oft einfacher korrekt zu verwenden sind.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    8. Oktober 11 um 13:40 Uhr

  • Ich mache mir wirklich Sorgen um das Timing-Modell. Es scheint, dass der Programmierer, der die Reihenfolge der Aktionen in autonomen Threads kennen muss, nicht “die richtige Abstraktionsebene” ist. Ich mag es nicht und ich habe es vermieden, die Signal- und Wartebefehle zu verwenden, weil es so aussieht, als würde es früher oder später einen Fehler verursachen.

    Benutzer2918461

    28. Februar 17 um 14:37 Uhr

Ich schreibe meine Antwort, weil ich nicht die sehe, die die Menschen beruhigen wird. Ich bin auch über diese bizarre, beunruhigende Warnung vor „logischen Fehlern“ in diesem Tutorial gestolpert. Beachten Sie, dass es nichts über diesen „Fehler“ in gibt den POSIX-Dokumentationsartikel auf pthread_cond_signal. Ich bin sicher, dass dies eine unglückliche Wahl des Begriffs oder ein einfacher Fehler des Autors des Tutorials ist. Ihre Behauptung kann so interpretiert werden, als würde ein Prozess in dieser Situation mit einem Fehler enden oder dass jedes Programm, das diese Situation zulässt, falsch ist. Nichts dergleichen ist wahr. Solche Situationen sind üblich. Das sagt die Dokumentation

Der pthread_cond_signal() und pthread_cond_broadcast() Funktionen haben keine Wirkung, wenn derzeit keine Threads blockiert sind cond.

Also mach dir keine Sorgen und sei glücklich.

  • Mit Abstand die beste Antwort, denke ich. Erstens beantwortet es wirklich die “Warum … Fehler” -Frage, während die akzeptierte und am meisten gewählte Frage dies nicht tut. Zweitens ist es einfach und leicht zu verstehen.

    – Renaud Pacalet

    28. Juli 2020 um 10:48 Uhr

  • @RenaudPacalet sehr späte Antwort auf Ihren Kommentar. Meine Antwort beantwortet die Frage zwar nicht direkt, verweist aber auf stackoverflow.com/a/5537231/104774 (Antwort von blaze), das die Frage bereits beantwortet. Ich denke, meine „Klarstellung“ hatte damals einige positive Stimmen, weil es nicht so offensichtlich war, wie Lebensläufe verwendet werden sollten. Es wurde manchmal als einfache Barriere verwendet, bei der der Empfänger nur darauf wartete, dass das Signal fortgesetzt wurde, was zu verwirrenden Fehlern führte. Übrigens scheint das Tutorial zu implizieren, dass der Mutex beim Aufruf gesperrt sein muss pthread_cond_signal was auch nicht stimmt.

    – stefaanv

    27. September 21 um 9:35 Uhr

Eine Bedingungsvariable ermöglicht es einem Thread, einen anderen aus einer Wartezeit aufzuwecken. Sie funktionieren nur, wenn in dem Moment, in dem Sie die Bedingung auslösen, ein Thread wartet. Um sicherzustellen, dass dies der Fall ist, sperrt der wartende Thread einen Mutex, der mit der Bedingung verknüpft ist, und der signalisierende Thread sperrt diesen Mutex, bevor er die Bedingung auslöst. Mit anderen Worten, der signalisierende Thread kann den Mutex nur sperren und die Bedingung auslösen, wenn der andere Thread den Mutex gesperrt hatte, aber jetzt wartet.

Ich bin am vertrautesten mit Boost, also werde ich das in diesem Beispiel verwenden:

// A shared mutex, global in this case.
boost::mutex myMutex;

// Condition variable
boost::condition_variable myCondition;

void threadProc()
{
    // Lock the mutex while the thread is running.
    boost::mutex::scoped_lock guard( myMutex );

    while( true )
    {
        // Do stuff, then...

        myCondition.wait( guard ); // Unlocks the mutex and waits for a notification.
    }
}

void func()
{
    // Function wants to trigger the other thread. Locks the mutex...
    boost::mutex::scoped_lock guard( myMutex );

    // Since the mutex is locked, we know that the other thread is
    // waiting on the condition variable...
    myCondition.notify_all();
}

Eine Bedingungsvariable zu signalisieren, wenn es kein entsprechendes Warten gibt, ist ein logischer Fehler, weil nichts jemals das Signal empfangen wird. Bedingungsvariablen bleiben nicht in einem signalisierten Zustand.

Wenn Sie sich nicht darum kümmern, dass dieses Signal verloren geht, liegt kein Fehler vor. Es ist nur ein Fehler, wenn Sie erwarten, dass ein später kommender wartender Thread sofort von cond_wait() aufwacht.

Da dies ein üblicher Anwendungsfall für pthread_cond ist, ruft das Tutorial diesen logischen Fehler auf. Aber nichts stürzt ab und es tritt kein unerwartetes Verhalten auf. Im normalen Ausführungsablauf kann cond_signal() immer noch ausgegeben werden, wenn es keine Threads in cond_wait() gibt: Beispielsweise führen alle Leser möglicherweise nur eine Nachrichtenverarbeitung durch, wenn der Schreiber ein weiteres Datenstück in die Warteschlange einfügt.

.

722150cookie-checkBedingungsvariable – warum ist der Aufruf von pthread_cond_signal() vor dem Aufruf von pthread_cond_wait() ein logischer Fehler?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy