Warum bekomme ich mit dispatch_once einen Deadlock?

7

Warum bin ich festgefahren?

%Vor%

Ich erwarte, dass foo beim ersten Aufruf zweimal ausgeführt wird.

    
user500 04.10.2013, 08:02
quelle

2 Antworten

22

Keine der vorhandenen Antworten ist ziemlich genau (eine ist völlig falsch, die andere ist ein bisschen irreführend und vermisst einige kritische Details). Zunächst gehen wir direkt zur Quelle :

%Vor%

Was wirklich passiert, ist, dass onceToken im Gegensatz zu den anderen Antworten von seinem Anfangszustand NULL auf eine Adresse auf dem Stack des ersten Aufrufers &dow (Aufruf dieses Aufrufers 1) geändert wird. . Dies geschieht bevor der Block aufgerufen wird. Wenn mehr Anrufer ankommen, bevor die Blockierung abgeschlossen ist, werden sie zu einer verketteten Liste von Kellnern hinzugefügt, deren Kopf in onceToken enthalten ist, bis der Block abgeschlossen ist (Anrufer anrufen 2..N). Nach dem Hinzufügen zu dieser Liste warten Anrufer 2..N auf einen Semaphor für Anrufer 1, um die Ausführung des Blocks abzuschließen, wobei Anrufer 1 die verkettete Liste durchlaufen wird, die den Semaphor einmal für jeden Anrufer 2..N signalisiert. Zu Beginn dieses Durchlaufs wird onceToken erneut geändert, um DISPATCH_ONCE_DONE zu sein (was zweckmäßigerweise als ein Wert definiert ist, der niemals ein gültiger Zeiger sein könnte und daher niemals der Kopf von sein könnte eine verkettete Liste geblockter Anrufer.) Wenn Sie sie in DISPATCH_ONCE_DONE ändern, ist es für nachfolgende Anrufer (für den Rest der Lebensdauer des Prozesses) günstig, den abgeschlossenen Status zu überprüfen.

In Ihrem Fall ist das also:

  • Wenn Sie -foo zum ersten Mal aufrufen, ist onceToken null (was garantiert ist, weil die Statik garantiert auf 0 initialisiert wird) und wird atomar geändert, um zum Kopf der verknüpften Liste von Kellnern zu werden.
  • Wenn Sie -foo rekursiv aus dem Block heraus aufrufen, wird Ihr Thread als "zweiter Aufrufer" betrachtet und eine Kellnerstruktur, die in diesem neuen, niedrigeren Stapelrahmen existiert, wird zur Liste hinzugefügt und Sie gehen auf den Semaphor warten.
  • Das Problem hierbei ist, dass dieser Semaphor niemals signalisiert wird, denn damit er signalisiert werden kann, müsste der Block fertig ausgeführt werden (im höheren Stapelrahmen), was nun aufgrund eines Deadlocks nicht mehr möglich ist.

Kurz gesagt, ja, Sie sind festgefahren, und das praktische Mitnehmen hier ist: "Versuchen Sie nicht, rekursiv in einen dispatch_once -Block zu rufen." Aber das Problem ist definitiv NICHT "unendliche Rekursion", und das Flag wird definitiv nicht nur geändert, nachdem der Block die Ausführung beendet hat - vor Der Block wird ausgeführt, genau wie er Caller 2.N kennen kann. Warten Sie, bis der Aufrufer 1 beendet ist.

    
ipmcc 04.10.2013, 11:24
quelle
2

Sie könnten den Code ein wenig ändern, so dass die Aufrufe außerhalb des Blocks liegen und es kein Deadlock gibt, etwa so:

%Vor%     
NiñoScript 23.12.2013 19:03
quelle