Warum erfordern die Bedingungsvariablenfunktionen von pthreads einen Mutex?


182

Ich lese weiter pthread.h; Die auf Bedingungsvariablen bezogenen Funktionen (wie pthread_cond_wait(3)) erfordern einen Mutex als Argument. Warum? Soweit ich das beurteilen kann, werde ich einen Mutex zu erschaffen nur als dieses Argument zu benutzen? Was soll dieser Mutex tun?

Antworten:


194

Es ist nur die Art und Weise, wie Bedingungsvariablen implementiert werden (oder ursprünglich).

Der Mutex wird verwendet, um die Bedingungsvariable selbst zu schützen . Deshalb muss es gesperrt sein, bevor Sie warten.

Durch das Warten wird der Mutex "atomar" entsperrt, sodass andere auf die Bedingungsvariable zugreifen können (zur Signalisierung). Wenn dann die Bedingungsvariable signalisiert oder gesendet wird, werden einer oder mehrere der Threads auf der Warteliste aufgeweckt und der Mutex wird für diesen Thread wieder magisch gesperrt.

In der Regel wird die folgende Operation mit Bedingungsvariablen angezeigt, die ihre Funktionsweise veranschaulicht. Das folgende Beispiel ist ein Arbeitsthread, der über ein Signal an eine Bedingungsvariable Arbeit erhält.

thread:
    initialise.
    lock mutex.
    while thread not told to stop working:
        wait on condvar using mutex.
        if work is available to be done:
            do the work.
    unlock mutex.
    clean up.
    exit thread.

Die Arbeit wird innerhalb dieser Schleife erledigt, vorausgesetzt, es sind einige verfügbar, wenn die Wartezeit zurückkehrt. Wenn der Thread markiert wurde, um die Arbeit zu beenden (normalerweise durch einen anderen Thread, der die Beendigungsbedingung festlegt und dann die Bedingungsvariable aufruft, um diesen Thread aufzuwecken), wird die Schleife beendet, der Mutex wird entsperrt und dieser Thread wird beendet.

Der obige Code ist ein Einzelverbrauchermodell, da der Mutex während der Arbeit gesperrt bleibt. Für eine Multi-Consumer-Variante können Sie als Beispiel Folgendes verwenden :

thread:
    initialise.
    lock mutex.
    while thread not told to stop working:
        wait on condvar using mutex.
        if work is available to be done:
            copy work to thread local storage.
            unlock mutex.
            do the work.
            lock mutex.
    unlock mutex.
    clean up.
    exit thread.

Dies ermöglicht anderen Verbrauchern, Arbeit zu erhalten, während dieser Arbeit erledigt.

Die Bedingungsvariable entlastet Sie von der Last, eine Bedingung abzufragen, anstatt dass ein anderer Thread Sie benachrichtigt, wenn etwas passieren muss. Ein anderer Thread kann diesem Thread mitteilen, dass diese Arbeit wie folgt verfügbar ist:

lock mutex.
flag work as available.
signal condition variable.
unlock mutex.

Die überwiegende Mehrheit der häufig fälschlicherweise als unechte Aufweckvorgänge bezeichneten Probleme war im Allgemeinen immer darauf zurückzuführen, dass innerhalb ihres pthread_cond_waitAnrufs (Broadcast) mehrere Threads signalisiert wurden. Man kehrte mit dem Mutex zurück, erledigte die Arbeit und wartete erneut.

Dann konnte der zweite signalisierte Thread herauskommen, wenn keine Arbeit zu erledigen war. Sie mussten also eine zusätzliche Variable haben, die angibt, dass die Arbeit erledigt werden soll (dies war von Natur aus mutexgeschützt mit dem Condvar / Mutex-Paar hier - andere Threads mussten den Mutex sperren, bevor er geändert wurde).

Es war technisch möglich, dass ein Thread von einer Wartezeit zurückkehrt, ohne von einem anderen Prozess ausgelöst zu werden (dies ist ein echtes falsches Aufwecken), aber in all meinen vielen Jahren, in denen ich an Pthreads gearbeitet habe, sowohl bei der Entwicklung / beim Service des Codes als auch als Benutzer von ihnen habe ich nie eine davon erhalten. Vielleicht lag das nur daran, dass HP eine anständige Implementierung hatte :-)

In jedem Fall behandelte derselbe Code, der den fehlerhaften Fall behandelt hat, auch echte falsche Aufweckvorgänge, da das Flag für verfügbare Arbeit für diese nicht gesetzt wäre.


3
'etwas tun' sollte sich nicht in der while-Schleife befinden. Sie möchten, dass Ihre while-Schleife nur den Zustand überprüft, andernfalls können Sie auch etwas tun, wenn Sie ein falsches Aufwecken erhalten.
Nr.

1
Nein, die Fehlerbehandlung steht an zweiter Stelle. Mit pthreads können Sie ohne ersichtlichen Grund (ein falsches Aufwecken) und ohne Fehler geweckt werden. Daher müssen Sie nach dem Aufwachen erneut einen Zustand überprüfen.
Nr.

1
Ich bin mir nicht sicher ob ich das verstehe. Ich hatte die gleiche Reaktion wie nos ; Warum ist do somethingin der whileSchleife?
ELLIOTTCABLE

1
Vielleicht mache ich es nicht klar genug. Die Schleife soll nicht warten, bis die Arbeit fertig ist, damit Sie es tun können. Die Schleife ist die Hauptarbeitsschleife "unendlich". Wenn Sie von cond_wait zurückkehren und das Arbeitsflag gesetzt ist, erledigen Sie die Arbeit und wiederholen die Schleife. "while some condition" ist nur dann falsch, wenn der Thread die Arbeit einstellen soll. An diesem Punkt wird der Mutex freigegeben und höchstwahrscheinlich beendet.
Paxdiablo

7
@stefaanv "Der Mutex soll die Bedingungsvariable immer noch schützen, es gibt keine andere Möglichkeit, sie zu schützen": Der Mutex soll die Bedingungsvariable nicht schützen. Es dient dem Schutz der Prädikatdaten , aber ich denke, Sie wissen das, indem Sie Ihren Kommentar lesen, der auf diese Aussage folgte. Sie können eine Bedingungsvariable legal signalisieren und durch Implementierungen nach dem Entsperren des Mutex, der das Prädikat umschließt , vollständig unterstützt werden. In einigen Fällen werden Sie in der Tat Konflikte entlasten .
WhozCraig

59

Eine Bedingungsvariable ist ziemlich begrenzt, wenn Sie nur eine Bedingung signalisieren können. Normalerweise müssen Sie einige Daten verarbeiten, die sich auf die signalisierte Bedingung beziehen. Signalisierung / Aufwecken müssen atomar erfolgen, um dies zu erreichen, ohne Rennbedingungen einzuführen, oder übermäßig komplex sein

pthreads können aus technischen Gründen auch zu einem falschen Aufwecken führen . Das bedeutet, dass Sie ein Prädikat überprüfen müssen, damit Sie sicher sein können, dass der Zustand tatsächlich signalisiert wurde - und dies von einem falschen Aufwecken unterscheiden können. Das Überprüfen einer solchen Bedingung hinsichtlich des Wartens darauf muss geschützt werden. Daher muss eine Bedingungsvariable eine Möglichkeit zum atomaren Warten / Aufwachen haben, während ein Mutex gesperrt / entsperrt wird, der diese Bedingung schützt.

Stellen Sie sich ein einfaches Beispiel vor, in dem Sie benachrichtigt werden, dass einige Daten erstellt wurden. Möglicherweise hat ein anderer Thread einige Daten erstellt, die Sie möchten, und einen Zeiger auf diese Daten gesetzt.

Stellen Sie sich einen Producer-Thread vor, der über einen Zeiger 'some_data' einige Daten an einen anderen Consumer-Thread weitergibt.

while(1) {
    pthread_cond_wait(&cond); //imagine cond_wait did not have a mutex
    char *data = some_data;
    some_data = NULL;
    handle(data);
}

Sie würden natürlich eine Menge Rennbedingungen bekommen, was wäre, wenn der andere Thread some_data = new_datadirekt nach dem Aufwachen, aber bevor Sie es tun würdendata = some_data

Sie können auch keinen eigenen Mutex erstellen, um diesen Fall zu schützen .eg

while(1) {

    pthread_cond_wait(&cond); //imagine cond_wait did not have a mutex
    pthread_mutex_lock(&mutex);
    char *data = some_data;
    some_data = NULL;
    pthread_mutex_unlock(&mutex);
    handle(data);
}

Funktioniert nicht, es besteht immer noch die Möglichkeit einer Rennbedingung zwischen dem Aufwachen und dem Ergreifen des Mutex. Das Platzieren des Mutex vor dem pthread_cond_wait hilft Ihnen nicht, da Sie den Mutex jetzt während des Wartens halten - dh der Produzent wird niemals in der Lage sein, den Mutex zu greifen. (Beachten Sie, dass Sie in diesem Fall eine zweite Bedingungsvariable erstellen können, um dem Produzenten zu signalisieren, dass Sie damit fertig sind. some_dataDies wird jedoch komplex, insbesondere wenn Sie viele Produzenten / Konsumenten wünschen.)

Daher benötigen Sie eine Möglichkeit, den Mutex atomar freizugeben / zu ergreifen, wenn Sie warten / aus dem Zustand aufwachen. Das ist es, was pthread-Bedingungsvariablen tun, und hier ist, was Sie tun würden:

while(1) {
    pthread_mutex_lock(&mutex);
    while(some_data == NULL) { // predicate to acccount for spurious wakeups,would also 
                               // make it robust if there were several consumers
       pthread_cond_wait(&cond,&mutex); //atomically lock/unlock mutex
    }

    char *data = some_data;
    some_data = NULL;
    pthread_mutex_unlock(&mutex);
    handle(data);
}

(Der Produzent müsste natürlich die gleichen Vorsichtsmaßnahmen treffen, immer 'some_data' mit dem gleichen Mutex schützen und sicherstellen, dass some_data nicht überschrieben wird, wenn some_data aktuell ist! = NULL)


Sollte das while (some_data != NULL)nicht eine Do-While-Schleife sein, damit sie mindestens einmal auf die Bedingungsvariable wartet?
Richter Maygarden

3
Nein. Worauf Sie wirklich warten, ist, dass 'some_data' nicht null ist. Wenn es beim "ersten Mal" nicht null ist, halten Sie den Mutex und können die Daten sicher verwenden. Wenn Sie eine do / while-Schleife hätten, würden Sie die Benachrichtigung verpassen, wenn jemand die Bedingungsvariable signalisierte, bevor Sie darauf warteten (es ist nichts anderes als die Ereignisse auf win32, die signalisiert bleiben, bis jemand auf sie wartet)
Nr.

4
Ich bin gerade über diese Frage gestolpert und ehrlich gesagt ist es seltsam festzustellen, dass diese Antwort, die einfach richtig ist, so viel weniger Punkte hat als die Antwort von paxdiablo, die bestimmte Fehler aufweist (Atomizität wird immer noch benötigt, der Mutex wird nur für die Behandlung der Bedingung benötigt). nicht zur Handhabung oder Benachrichtigung). Ich denke, genau so funktioniert Stackoverflow ...
Stefaanv

@stefaanv, wenn Sie die Fehler als Kommentare zu meiner Antwort detaillieren möchten, damit ich sie rechtzeitig sehe, anstatt Monate später :-), werde ich sie gerne beheben. Ihre kurzen Sätze geben mir nicht genug Details, um herauszufinden, was Sie sagen wollen.
Paxdiablo

1
@nos, sollte nicht while(some_data != NULL)sein while(some_data == NULL)?
Eric Z

30

POSIX-Bedingungsvariablen sind zustandslos. Es liegt also in Ihrer Verantwortung, den Staat aufrechtzuerhalten. Da sowohl wartende Threads als auch Threads, die anderen Threads anweisen, nicht mehr zu warten, auf den Status zugreifen, muss er durch einen Mutex geschützt werden. Wenn Sie glauben, dass Sie Bedingungsvariablen ohne Mutex verwenden können, haben Sie nicht verstanden, dass Bedingungsvariablen zustandslos sind.

Bedingungsvariablen basieren auf einer Bedingung. Threads, die auf eine Bedingungsvariable warten, warten auf eine Bedingung. Threads, die Bedingungsvariablen signalisieren, ändern diese Bedingung. Beispielsweise wartet ein Thread möglicherweise auf das Eintreffen einiger Daten. Ein anderer Thread bemerkt möglicherweise, dass die Daten angekommen sind. "Die Daten sind angekommen" ist die Bedingung.

Hier ist die klassische Verwendung einer bedingten Bedingungsvariablen:

while(1)
{
    pthread_mutex_lock(&work_mutex);

    while (work_queue_empty())       // wait for work
       pthread_cond_wait(&work_cv, &work_mutex);

    work = get_work_from_queue();    // get work

    pthread_mutex_unlock(&work_mutex);

    do_work(work);                   // do that work
}

Sehen Sie, wie der Thread auf die Arbeit wartet. Die Arbeit ist durch einen Mutex geschützt. Das Warten gibt den Mutex frei, damit ein anderer Thread diesem Thread etwas Arbeit geben kann. So würde es signalisiert werden:

void AssignWork(WorkItem work)
{
    pthread_mutex_lock(&work_mutex);

    add_work_to_queue(work);           // put work item on queue

    pthread_cond_signal(&work_cv);     // wake worker thread

    pthread_mutex_unlock(&work_mutex);
}

Beachten Sie, dass Sie den Mutex benötigen , um die Arbeitswarteschlange zu schützen. Beachten Sie, dass die Bedingungsvariable selbst keine Ahnung hat, ob Arbeit vorhanden ist oder nicht. Das heißt, eine Bedingungsvariable muss einer Bedingung zugeordnet sein, diese Bedingung muss von Ihrem Code verwaltet werden, und da sie von Threads gemeinsam genutzt wird, muss sie durch einen Mutex geschützt werden.


1
Genauer gesagt besteht der gesamte Punkt der Bedingungsvariablen darin, eine atomare Operation zum Entsperren und Warten bereitzustellen. Ohne einen Mutex gäbe es nichts zu entsperren.
David Schwartz

Würde es Ihnen etwas ausmachen, die Bedeutung von Staatenlos zu erklären ?
Snr

@snr Sie haben keinen Staat. Sie sind nicht "gesperrt" oder "signalisiert" oder "nicht signalisiert". Es liegt also in Ihrer Verantwortung, den Status der Bedingungsvariablen zu verfolgen. Wenn die Bedingungsvariable beispielsweise einen Thread darüber informiert, wenn eine Warteschlange nicht leer wird, muss ein Thread die Warteschlange nicht leer machen, und ein anderer Thread muss wissen, wann die Warteschlange nicht leer wird. Das ist ein gemeinsamer Zustand, und Sie müssen ihn mit einem Mutex schützen. Sie können die Bedingungsvariable in Verbindung mit dem durch einen Mutex geschützten gemeinsamen Status als Aufweckmechanismus verwenden.
David Schwartz

16

Nicht alle Bedingungsvariablenfunktionen erfordern einen Mutex: Nur die Warteoperationen. Die Signal- und Rundfunkoperationen erfordern keinen Mutex. Eine Bedingungsvariable ist auch keinem bestimmten Mutex dauerhaft zugeordnet. Der externe Mutex schützt die Bedingungsvariable nicht. Wenn eine Bedingungsvariable einen internen Status hat, z. B. eine Warteschlange wartender Threads, muss dies durch eine interne Sperre innerhalb der Bedingungsvariablen geschützt werden.

Die Warteoperationen bringen eine Bedingungsvariable und einen Mutex zusammen, weil:

  • Ein Thread hat den Mutex gesperrt, einen Ausdruck über gemeinsam genutzte Variablen ausgewertet und festgestellt, dass er falsch ist, sodass er warten muss.
  • Der Thread muss sich atomar vom Besitz des Mutex zum Warten auf die Bedingung bewegen.

Aus diesem Grund verwendet die Warteoperation sowohl den Mutex als auch die Bedingung als Argumente: Damit kann die atomare Übertragung eines Threads vom Besitz des Mutex zum Warten verwaltet werden, sodass der Thread nicht der verlorenen Wecklaufbedingung zum Opfer fällt .

Eine verlorene Wakeup-Race-Bedingung tritt auf, wenn ein Thread einen Mutex aufgibt und dann auf ein zustandsloses Synchronisationsobjekt wartet, jedoch auf eine Weise, die nicht atomar ist: Es gibt ein Zeitfenster, in dem der Thread nicht mehr gesperrt ist und hat noch nicht begonnen, auf das Objekt zu warten. Während dieses Fensters kann ein anderer Thread eingehen, die erwartete Bedingung erfüllen, die zustandslose Synchronisation signalisieren und dann verschwinden. Das zustandslose Objekt erinnert sich nicht daran, dass es signalisiert wurde (es ist zustandslos). Dann geht der ursprüngliche Thread auf dem zustandslosen Synchronisationsobjekt in den Ruhezustand und wird nicht aktiviert, obwohl die Bedingung, die er benötigt, bereits erfüllt ist: verlorenes Aufwecken.

Die Wartefunktionen der Bedingungsvariablen vermeiden das verlorene Aufwecken, indem sie sicherstellen, dass der aufrufende Thread registriert ist, um das Aufwecken zuverlässig abzufangen, bevor er den Mutex aufgibt. Dies wäre unmöglich, wenn die Bedingungsfunktion für Bedingungsvariablen den Mutex nicht als Argument verwenden würde.


Können Sie einen Hinweis geben, dass Broadcast-Vorgänge nicht erforderlich sind, um den Mutex zu erwerben? Bei MSVC wird die Übertragung ignoriert.
Xvan

@xvan Das POSIX pthread_cond_broadcastund die pthread_cond_signalOperationen (um die es in dieser SO-Frage geht) nehmen nicht einmal den Mutex als Argument; nur die Bedingung. Die POSIX-Spezifikation ist hier . Der Mutex wird nur in Bezug auf das erwähnt, was in den wartenden Threads passiert, wenn sie aufwachen.
Kaz

Würde es Ihnen etwas ausmachen, die Bedeutung von Staatenlos zu erklären ?
Snr

1
@snr Ein zustandsloses Synchronisationsobjekt kann sich an keinen Status im Zusammenhang mit der Signalisierung erinnern. Wenn signalisiert, wenn jetzt etwas darauf wartet, wird es aufgeweckt, andernfalls wird das Aufwecken vergessen. Bedingungsvariablen sind so zustandslos. Der notwendige Zustand, um die Synchronisation zuverlässig zu machen, wird von der Anwendung aufrechterhalten und durch den Mutex geschützt, der in Verbindung mit den Bedingungsvariablen gemäß korrekt geschriebener Logik verwendet wird.
Kaz

7

Ich finde die anderen Antworten nicht so präzise und lesbar wie diese Seite . Normalerweise sieht der Wartecode ungefähr so ​​aus:

mutex.lock()
while(!check())
    condition.wait()
mutex.unlock()

Es gibt drei Gründe, das wait()in einen Mutex zu wickeln :

  1. ohne mutex könnte ein anderer thread signal()vor demwait() und wir würden dieses Aufwachen verpassen.
  2. Normalerweise check()hängt es von Änderungen an einem anderen Thread ab, daher müssen Sie ihn trotzdem gegenseitig ausschließen.
  3. um sicherzustellen, dass der Thread mit der höchsten Priorität zuerst fortgesetzt wird (die Warteschlange für den Mutex ermöglicht es dem Scheduler, zu entscheiden, wer als nächstes geht).

Der dritte Punkt ist nicht immer ein Problem - der historische Kontext ist vom Artikel aus mit diesem Gespräch verknüpft .

In Bezug auf diesen Mechanismus werden häufig falsche Aufweckvorgänge erwähnt (dh der wartende Thread wird geweckt, ohne signal()aufgerufen zu werden). Solche Ereignisse werden jedoch von der Schleife behandelt check().


4

Bedingungsvariablen sind mit einem Mutex verknüpft, da nur so die Rasse vermieden werden kann, die er vermeiden soll.

// incorrect usage:
// thread 1:
while (notDone) {
    pthread_mutex_lock(&mutex);
    bool ready = protectedReadyToRunVariable
    pthread_mutex_unlock(&mutex);
    if (ready) {
        doWork();
    } else {
        pthread_cond_wait(&cond1); // invalid syntax: this SHOULD have a mutex
    }
}

// signalling thread
// thread 2:
prepareToRunThread1();
pthread_mutex_lock(&mutex);
   protectedReadyToRuNVariable = true;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond1);

Now, lets look at a particularly nasty interleaving of these operations

pthread_mutex_lock(&mutex);
bool ready = protectedReadyToRunVariable;
pthread_mutex_unlock(&mutex);
                                 pthread_mutex_lock(&mutex);
                                 protectedReadyToRuNVariable = true;
                                 pthread_mutex_unlock(&mutex);
                                 pthread_cond_signal(&cond1);
if (ready) {
pthread_cond_wait(&cond1); // uh o!

Zu diesem Zeitpunkt gibt es keinen Thread, der die Bedingungsvariable signalisiert, sodass Thread1 für immer warten wird, obwohl die protectedReadyToRunVariable angibt, dass es einsatzbereit ist!

Die einzige Möglichkeit, dies zu umgehen, besteht darin, dass Bedingungsvariablen den Mutex atomar freigeben und gleichzeitig auf die Bedingungsvariable warten. Aus diesem Grund erfordert die Funktion cond_wait einen Mutex

// correct usage:
// thread 1:
while (notDone) {
    pthread_mutex_lock(&mutex);
    bool ready = protectedReadyToRunVariable
    if (ready) {
        pthread_mutex_unlock(&mutex);
        doWork();
    } else {
        pthread_cond_wait(&mutex, &cond1);
    }
}

// signalling thread
// thread 2:
prepareToRunThread1();
pthread_mutex_lock(&mutex);
   protectedReadyToRuNVariable = true;
   pthread_cond_signal(&mutex, &cond1);
pthread_mutex_unlock(&mutex);

3

Der Mutex soll gesperrt sein, wenn Sie anrufen pthread_cond_wait . Wenn Sie es atomar aufrufen, wird sowohl der Mutex entsperrt als auch die Bedingung blockiert. Sobald der Zustand signalisiert ist, sperrt er ihn wieder atomar und kehrt zurück.

Dies ermöglicht die Implementierung einer vorhersagbaren Planung, falls gewünscht, indem der Thread, der die Signalisierung durchführen würde, warten kann, bis der Mutex freigegeben wird, um seine Verarbeitung durchzuführen, und dann den Zustand signalisieren kann.


Also ... gibt es einen Grund für mich , den Mutex nicht einfach immer unverschlossen zu lassen und ihn dann direkt vor dem Warten zu sperren und ihn dann direkt nach Beendigung des Wartens zu entsperren?
ELLIOTTCABLE

Der Mutex löst auch einige mögliche Rennen zwischen dem wartenden und dem signalisierenden Thread. Solange der Mutex beim Ändern des Zustands und der Signalisierung immer gesperrt ist, werden Sie nie feststellen, dass Sie das Signal verpassen und für immer schlafen
Hasturkun

Also ... sollte ich zuerst auf den Mutex des Conditionvars warten, bevor ich auf den Conditionvar warte? Ich bin mir nicht sicher, ob ich das überhaupt verstehe.
ELLIOTTCABLE

2
@elliottcable: Ohne den Mutex zu halten, wie kannst du wissen, ob du warten sollst oder nicht? Was ist, wenn das, worauf Sie warten, gerade passiert ist?
David Schwartz

1

Ich habe eine Übung im Unterricht gemacht, wenn Sie ein echtes Beispiel für eine Bedingungsvariable wollen:

#include "stdio.h"
#include "stdlib.h"
#include "pthread.h"
#include "unistd.h"

int compteur = 0;
pthread_cond_t varCond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex_compteur;

void attenteSeuil(arg)
{
    pthread_mutex_lock(&mutex_compteur);
        while(compteur < 10)
        {
            printf("Compteur : %d<10 so i am waiting...\n", compteur);
            pthread_cond_wait(&varCond, &mutex_compteur);
        }
        printf("I waited nicely and now the compteur = %d\n", compteur);
    pthread_mutex_unlock(&mutex_compteur);
    pthread_exit(NULL);
}

void incrementCompteur(arg)
{
    while(1)
    {
        pthread_mutex_lock(&mutex_compteur);

            if(compteur == 10)
            {
                printf("Compteur = 10\n");
                pthread_cond_signal(&varCond);
                pthread_mutex_unlock(&mutex_compteur);
                pthread_exit(NULL);
            }
            else
            {
                printf("Compteur ++\n");
                compteur++;
            }

        pthread_mutex_unlock(&mutex_compteur);
    }
}

int main(int argc, char const *argv[])
{
    int i;
    pthread_t threads[2];

    pthread_mutex_init(&mutex_compteur, NULL);

    pthread_create(&threads[0], NULL, incrementCompteur, NULL);
    pthread_create(&threads[1], NULL, attenteSeuil, NULL);

    pthread_exit(NULL);
}

1

Es scheint eher eine spezifische Entwurfsentscheidung als eine konzeptionelle Notwendigkeit zu sein.

Laut den pthreads-Dokumenten ist der Grund dafür, dass der Mutex nicht getrennt wurde, dass es eine signifikante Leistungsverbesserung gibt, wenn sie kombiniert werden, und sie erwarten, dass aufgrund der allgemeinen Rennbedingungen, wenn Sie keinen Mutex verwenden, dies fast immer sowieso getan wird.

https://linux.die.net/man/3/pthread_cond_wait

Merkmale von Mutexen und Bedingungsvariablen

Es wurde vorgeschlagen, die Mutex-Erfassung und -Freigabe vom Bedingungszustand zu entkoppeln. Dies wurde abgelehnt, da es die kombinierte Natur des Vorgangs ist, die tatsächlich Echtzeitimplementierungen erleichtert. Diese Implementierungen können einen Thread mit hoher Priorität atomar zwischen der Bedingungsvariablen und dem Mutex auf eine Weise verschieben, die für den Aufrufer transparent ist. Dies kann zusätzliche Kontextwechsel verhindern und eine deterministischere Erfassung eines Mutex ermöglichen, wenn der wartende Thread signalisiert wird. Somit können Fairness- und Prioritätsprobleme direkt von der Planungsdisziplin behandelt werden. Darüber hinaus entspricht die Warteoperation für den aktuellen Zustand der bestehenden Praxis.


0

Es gibt eine Menge Exegesen darüber, aber ich möchte es mit einem folgenden Beispiel verkörpern.

1 void thr_child() {
2    done = 1;
3    pthread_cond_signal(&c);
4 }

5 void thr_parent() {
6    if (done == 0)
7        pthread_cond_wait(&c);
8 }

Was ist los mit dem Code-Snippet? Denken Sie einfach etwas nach, bevor Sie fortfahren.


Das Problem ist wirklich subtil. Wenn der Elternteil thr_parent()den Wert von aufruft und dann überprüft done, sieht er, dass dies der Fall ist, 0und versucht daher, einzuschlafen. Doch kurz bevor der Anruf wartet, um schlafen zu gehen, wird der Elternteil zwischen den Zeilen 6-7 unterbrochen und das Kind rennt. Das Kind ändert die Statusvariable donein1 und signalisiert, aber es wartet kein Thread und somit wird kein Thread geweckt. Wenn der Elternteil wieder rennt, schläft er für immer, was wirklich ungeheuerlich ist.

Was ist, wenn sie ausgeführt werden, während die Schlösser einzeln erworben wurden?

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.