Wenn ein bereits abgebrochenes CancellationToken übergeben wird, wird HttpClient angehalten

8

Ich möchte CancellationToken verwenden, um einen Aufruf von HttpClient.PostAsJsonAsync abzubrechen. Mit dem folgenden Setup hängt der Aufruf von PostAsJsonAsync jedoch unbegrenzt (ich habe ihn für einige Minuten laufen lassen).

%Vor%

Beachten Sie, dass ich ein bereits abgebrochenes CancellationTokenSource übergebe - ich habe das gleiche Problem, wenn ich das Token mit Task.Delay mit einer kurzen Verzögerung abbreche.

Ich weiß, ich könnte einfach überprüfen, ob das Token vor dem Aufruf abgebrochen wurde, aber trotzdem habe ich das gleiche Problem, wenn das Token nach einer kurzen Verzögerung abgebrochen wird, dh es wird nicht vor dem Methodenaufruf abgebrochen, sondern wird so kurz danach.

Meine Frage ist also, was verursacht das und was kann ich tun, um es zu umgehen / reparieren?

Bearbeiten

Für diejenigen, die nach einem Workaround suchen, der von @Darrel Millers Antwort inspiriert wurde, habe ich die folgende Erweiterungsmethode entwickelt:

%Vor%     
nick_w 15.04.2014, 05:23
quelle

2 Antworten

7

Es scheint definitiv ein Fehler zu sein, den Sie treffen. Sie können umgehen, indem Sie das HttpContent / ObjectContent-Objekt selbst konstruieren, so wie dies.

%Vor%

Der Aufruf von content.LoadIntoBufferAsync erzwingt die Deserialisierung vor dem PostAsync und scheint den Deadlock zu vermeiden.

    
Darrel Miller 15.04.2014 14:09
quelle
6

Stimmen Sie der Antwort von @Darrel Miller zu. Dies ist ein Fehler. Fügen Sie einfach mehr Details für den Fehlerbericht hinzu.

Das Problem ist, dass intern ein TaskCompletionSource verwendet wird, aber wenn eine Ausnahme aufgrund der Stornierung in diesem speziellen Fall ausgelöst wird, wird es nicht abgefangen, und der TaskCompletionSource wird nie in einen der abgeschlossenen Zustände gesetzt ( und daher wird das Warten auf die TaskCompletionSource Task niemals zurückkehren.

Wenn Sie ILSpy verwenden und HttpClientHandler.SendAsync betrachten, sehen Sie TaskCompletionSource :

%Vor%

Später, durch die Zeile Task.Factory.StartNew(this.startRequest, requestState); , kommen wir zur folgenden Methode:

%Vor%

Sie werden feststellen, dass der Delegat im Aufruf von ContinueWithStandard keine Ausnahmebehandlung innerhalb des Delegaten hat, und niemand hält sich an der zurückgegebenen Aufgabe fest (und wenn diese Aufgabe daher eine Ausnahme auslöst, wird sie ignoriert). Der Aufruf von this.StartGettingRequestStream(state); löst eine Ausnahme aus:

%Vor%

Hier ist der vollständige Callstack zum Zeitpunkt der Ausnahme:

%Vor%

Ich glaube, die Absicht ist es nicht zu ignorieren, und im Falle einer Ausnahme rufen Sie die Methode HandleAsyncException auf, die TaskCompletionSource in einen endgültigen Zustand versetzt.

    
Matt Smith 15.04.2014 15:02
quelle