Detaillierte Erklärung von pthread_cond_t unter Linux

Auf einen Blick

 


Zweck dieses Artikels

 
  Zunächst geht es in diesem Artikel nicht darum, bedingte Variablen zu verwenden. Hier liste ich zuerst einen   solchen Absatz über die Funktion pthread_cond_wait in apue auf :

  • "Der Aufrufer übergibt den gesperrten Mutex an die Funktion , und die Funktion setzt den aufrufenden Thread automatisch in die Liste der Threads, die auf die Bedingung warten. ** Entsperrt den Mutex. ** Dadurch wird die Bedingungsprüfung deaktiviert und der Thread wird in den Ruhezustand versetzt Damit die Bedingung den Zeitkanal zwischen diesen beiden Operationen ändert, damit der Thread keine Änderungen an der Bedingung übersieht . Wenn pthread_cond_wait zurückkehrt, wird der Mutex erneut gesperrt. "

  Die Informationsmenge in dieser Passage ist sehr groß, und die Funktionsweise des Mutex kann als die folgenden drei Punkte verstanden werden:

  1. Bevor Sie pthread_cond_wait aufrufen, müssen Sie den Mutex-Mutex sperren, bevor Sie & mutex an die Funktion pthread_cond_wait übergeben
  2. Innerhalb der Funktion pthread_cond_wait wird der eingehende Mutex zuerst entsperrt
  3. Wenn die Wartebedingung eintritt, sperrt die Funktion pthread_cond_wait den eingehenden Mutex, bevor er zurückkehrt

  Ich habe damals alle möglichen Fragen gesehen. Warum wurde es gesperrt, bevor es übergeben wurde, warum wurde es freigegeben, nachdem es übergeben wurde, und warum wurde es wieder gesperrt, als es zurückkam?

In diesem Artikel werden diese drei Probleme ausführlich erläutert. Aber vorher müssen wir verstehen, warum es bedingte Variablen gibt. Das heißt, die Rolle von Bedingungsvariablen.


Warum Bedingungsvariablen benötigt werden

 
  Wenn es keine bedingte Variable gibt, warten wir, bis eine Bedingung erfüllt ist. Dann werden wir das folgende Modell sein:
Fügen Sie hier eine Bildbeschreibung ein
  Zuerst sperren und den kritischen Bereich betreten, um zu überprüfen, ob die Bedingung erfüllt ist, wenn nicht erfüllt, entsperren und den kritischen Bereich verlassen Schlafen Sie eine Zeit lang und fahren Sie dann mit dem Urteil fort. Wenn Sie in diesem Fall nur den kritischen Abschnitt verlassen und die Bedingung erfüllt ist, muss der Thread einige Zeit warten, bis er wieder in den kritischen Abschnitt eingeht, um zu wissen, dass die Bedingung erfüllt ist (wenn die Bedingung während dieses Zeitraums erfüllt bleibt). Wenn dies der Fall ist, wird die Bedingung für einen kurzen Zeitraum nicht erfüllt, dann wird dieser Thread weiterhin die Beurteilung durchlaufen. Durch ständiges Sperren und Entsperren (was sich auf andere Threads auswirkt, die dieselbe Sperre verwenden) wird die Bedingung beim ersten Mal nicht erfüllt. Dieses Modell ist zeitaufwändig und teuer.

  Daher wird die Bedingungsvariable nur generiert, um nicht zyklisch zu sperren und zu entsperren und um die Benachrichtigung zu erhalten, dass die Bedingung beim ersten Mal erfüllt ist.


Drei Fragen

 
  Um diese drei Fragen zu beantworten, müssen Sie zunächst die Koordination von Warten und Erwachen verstehen.

  Das Bild unten ist eine Korrektur, die ich vorgenommen habe, nachdem ich mich auf die Bilder anderer Personen bezogen habe (das Originalbild ist falsch). Tatsächlich kann diese Abbildung die drei Fragen erklären: Warum wird pthread_cond_wait gesperrt, bevor es übergeben wird, warum wird es entsperrt, nachdem es übergeben wurde, und warum wird es gesperrt, bevor es zurückgegeben wird. Aber lassen Sie mich das im Detail erklären.
Fügen Sie hier eine Bildbeschreibung ein
  In der Abbildung gibt es einen wichtigen Punkt, nämlich zu beurteilen, ob die Bedingung erfüllt ist. Vor dem Aufruf von pthread_cond_wait und nach dem Sperren kann pthread_cond_wait die Bedingungen nicht beurteilen, und wir müssen externe Beurteilungserklärungen schreiben.

  1. Wenn die Bedingung nicht erfüllt ist, wird pthread_cond_wait eingegeben
  2. Geben Sie pthread_cond_wait ein, um es sofort zu entsperren und zu blockieren
  3. pthread_cond_signal aktiviert den in pthread_cond_wait blockierten Prozess

Sie können den folgenden Code kombinieren, um ihn klarer zu gestalten.

Der folgende Pseudocode für die übliche Verwendung von pthread_cond_wait und pthread_cond_signal (Bedingung ist: Ist der Wert größer als 0):

	lock(&mutex);
	while(value<=0)//需要value>0所以 value<=0就条件不满足
	{
		pthread_cond_wait(&cond,&mutex);
	}
	unlock(&mutex);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
	lock(&mutex);
	if(value==0)
	{
		value++;
	}
	if(value>0)
	{
		pthread_cond_signal(&cond);
	}
	unlock(&mutex);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Nach Klärung dieses grundlegenden Prozesses können die drei Fragen erklärt werden.


Sperren Sie den Mutex vor dem Eingang

Fügen Sie hier eine Bildbeschreibung ein
  Um es allen leichter zu machen, sie anzuschauen, werde ich das obige Bild zur Erklärung jeder Frage erneut veröffentlichen.

Durch Sperren des Mutex vor dem Übergeben wird sichergestellt, dass die Bedingung nicht geändert wird, bevor der Thread anhand der Bedingung beurteilt, ob pthread_cond_wait eingegeben werden soll.

  Wenn vor dem Eingang keine Sperre vorhanden ist. Es wird eine solche Situation geben: Nachdem Thread A beurteilt hat, dass die Bedingung nicht erfüllt ist, bevor pthread_cond_wait aufgerufen wird, weil A schläft oder aufgrund der Ausführungsreihenfolge und Geschwindigkeit mehrerer Threads unter Multithreading, ändert Thread B die Bedingung, sodass die Bedingung ist befriedigt. Zu diesem Zeitpunkt hat Thread A jedoch noch nicht pthread_cond_wait aufgerufen. Nachdem Thread A erneut pthread_cond_wait aufruft, kann er, obwohl die Bedingung erfüllt ist, das Aufwecken von pthread_cond_signal nicht empfangen und blockiert weiter.


Entsperren Sie den Mutex nach dem Eingang

Fügen Sie hier eine Bildbeschreibung ein
Nach dem Eingang entsperren, damit die Bedingung geändert wird

  Das Entsperren nach dem Übergeben erfolgt, weil der Teil, der pthread_cond_signal aufruft, gesperrt und geändert werden muss, bevor pthread_cond_signal aufgerufen wird. (Ändern Sie die Bedingung und warten Sie, bis die Bedingung erfüllt ist. Dies gilt für die Konkurrenz der Bedingung. Daher benötigen die beiden Threads, die pthread_cond_wait und pthread_cond_signal aufrufen, dieselbe Sperre.)

  Wenn der Mutex in pthread_cond_wait nicht entsperrt ist, können andere Threads nach dem Aufrufen von pthread_cond_wait die Bedingung nicht ändern und die Bedingung wird nicht erfüllt.

Sperren Sie den Mutex erneut, bevor Sie zurückkehren

Fügen Sie hier eine Bildbeschreibung ein

  1. Durch erneutes Sperren des Mutex vor der Rückkehr wird sichergestellt, dass der Thread nach der Rückkehr von pthread_cond_wait zu nicht geändert wird, bevor die Bedingung erneut beurteilt wird.
  2. Stellen Sie sicher, dass andere Anweisungen ausgeführt werden können, die möglicherweise zwischen pthread_cond_signal und dem Freischaltmutex erforderlich sind

  Für 1 ähnelt der Grund hier dem Grund für das Sperren des Mutex vor dem Übergeben von pthread_cond_wait. Wenn es nicht gesperrt ist, ist die Bedingung erfüllt, nachdem Thread A pthread_cond_wait aufgerufen hat, Thread A wird aktiviert und kehrt von pthread_cond_wait zurück. Thread B ändert die Bedingung zu diesem Zeitpunkt, wodurch die Bedingung nicht erfüllt wird. Thread A weiß nicht, dass die Bedingung erneut geändert wird, oder denkt, dass die Bedingung erfüllt ist, es kann schief gehen.

  Für 2 gilt, solange zwischen pthread_cond_signal und dem Entsperren des Mutex andere Anweisungen ausgeführt werden müssen, da der Mutex zu diesem Zeitpunkt von diesem Thread gesperrt und nicht entsperrt wurde, das Verhalten des Threads, der pthread_cond_wait vor pthread_cond_wait aufgerufen hat Gibt zurück, bevor der Sperrmutex Will blockiert wird, bis die Anweisung nach dem Ausführen und Entsperren von pthread_cond_signal ausgeführt wird. Pthread_cond_wait wird nicht zurückgegeben.

Übrigens, da pthread_cond_wait wieder zum Sperren zurückkehrt, wird pthread_cond_signal nicht unbedingt in der Mitte von lock () und entsperren () platziert.

Zwei Möglichkeiten, pthread_cond_signal zu schreiben

lock(&mutex);
//一些操作
pthread_cond_signal(&cond);
//一些操作
unlock(&mutex);
  • 1
  • 2
  • 3
  • 4
  • 5

  Nachteile: Bei der Implementierung einiger Threads wird der wartende Thread (aufgrund von cond_signal) aus dem Kernel geweckt und kehrt in den Benutzerbereich zurück. Anschließend muss pthread_cond_wait vor der Rückkehr gesperrt werden, es wurde jedoch festgestellt, dass die Sperre nicht aufgehoben wurde freigegeben und kehrt zum Kernel-Bereich zurück. Sobald es Leistungsprobleme gibt.
  In LinuxThreads oder NPTL gibt es jedoch kein solches Problem, da es in Linux-Threads zwei Warteschlangen gibt, cond_wait queue und mutex_lock queue. Cond_signal ermöglicht dem Thread lediglich, von der cond_wait-Warteschlange zur mutex_lock-Warteschlange zu wechseln, ohne zum Benutzerbereich zurückzukehren kein Leistungsverlust sein. Es ist also kein Problem, es unter Linux zu verwenden.
 
 

lock(&mutex);
//一些操作
unlock(&mutex);
pthread_cond_signal(&cond);
  • 1
  • 2
  • 3
  • 4

Vorteile: Der zuvor erwähnte potenzielle Leistungsverlust tritt nicht auf, da die Sperre vor dem Signal aufgehoben wurde

Nachteile: Wenn der Prozessaustausch nach dem Entsperren und vor dem Signal erfolgt, erhält ein anderer Prozess (nicht der Prozess, der auf die Bedingung wartet) die begehrte Sperre und fügt dann die Sperroperation hinzu. Die Sperre wird dann von anderen zurückgenommen, wenn sie schließlich zum Thread wechselt Warten auf die Bedingung Nicht zurückgekehrt, kann nur weiter warten.


Epilog

  Bedingungsvariablen sollten häufiger verwendet werden, um ein klareres Verständnis zu erhalten. Nur die API zu betrachten, funktioniert immer noch nicht.

obenstehendes

Ich denke du magst

Origin blog.csdn.net/star871016/article/details/109642644
Empfohlen
Rangfolge