Ich hatte ein Problem mit einem Hanging have (beschrieben here). Während der Forschung fand ich heraus, dass der Aufruf SetResult
auf meinem TaskCompletionSource
aufruft tatsächlich wartet auf Fortsetzung im Kontext des Threads, der SetResult
genannt (dies wird auch in this answer zu einer etwas verwandten Frage geschrieben). In meinem Fall ist dies ein anderer Thread (Thread-Pool-Worker-Thread) als der, der das Warten gestartet hat (ein ASP.NET-Anforderungs-Thread).Manuelles Erfassen und Anwenden von SynchronizationContext beim Abschließen einer Task
Während ich immer noch nicht sicher bin, warum dies einen Hang verursachen würde, entschied ich mich zu versuchen, die SetResult
in den ursprünglichen Kontext zu zwingen. Ich habe den Wert SynchronizationContext.Current
vor der Eingabe auf den Anfragethread gespeichert und ihn im Worker-Thread über SynchronizationContext.SetSynchronizationContext
kurz vor dem Aufruf SetResult
manuell angewendet. Das hat den Hang gelöst und ich kann nun alle meine asynchronen Methoden abwarten, ohne ConfigureAwait(false)
angeben zu müssen.
Meine Frage ist: Ist das ein vernünftiger und korrekter Ansatz zur manuellen Erfassung und Anwendung der SynchronizationContext
? FWIW, ich habe versucht, eine einfache Post()
mit dem SetResult
Delegierten zuerst, aber das verursachte noch einen Hang. Ich bin hier offensichtlich ein wenig außerhalb meiner Komfortzone ... Bitte hilf mir zu verstehen, was vor sich geht!
Guten Punkt über die Wiederherstellung des Kontexts! In Bezug auf Garantien von SetResult (oder deren Fehlen) ... Könnten Sie bitte etwas erweitern? Der * beobachtete * Effekt ist, dass der manuell angewendete Kontext vom Worker-Thread zurückgeleitet wird zu der Fortsetzung ganz gut, so muss es von der SetResult, AFAIU getan werden. Wenn das nur ein Zufall ist, wie Sie angedeutet haben, würde ich gerne mehr über die düsteren Details wissen! – aoven
Die TPL hat ein paar Punkte, wo Code ist Inlineed für Effizienz.Dies ist schrecklich.Siehe https://github.com/dotnet/corefx/issues/2454.Wenn Sie Thread lokalen Status ändern und diese Aufrufkette haben: A => SetResult => B dann B wird diesen Zustand zu sehen Wenn die Kette A => SetResult => QueueToThreadPool (B) ist, wird B den sauberen Zustand sehen. Erklärt das, was Sie sehen? = – usr
Ich kann nicht so oder so etwas sagen ...Meine Kette ist eigentlich A => QueueToThreadPool => SetResult => B und ich habe jetzt festgestellt, dass SetResult die Fortsetzung nicht direkt aufruft, weil der Aufruf erfolgreich ist und der Worker-Thread nicht blockiert. Das einzige Ding (das für mich sichtbar ist) ist das Warten auf A. Das Übergeben von RunContinuationsAsynchron zu TaskCompletionSource scheint keine Wirkung zu haben, gleich mit dem Aufruf von Post/Send. Sie erwähnten bereits das Problem ist in der Fortsetzung ... Welche Art von Problem könnte es möglicherweise, wenn es nie einmal ausgeführt wird (der Block tritt auf der Warte Zeile)? – aoven