2016-11-25 1 views
1

Ich habe ein serverseitiges Klickereignis auf einer ASP.NET WebForms-Website. In diesem Fall rufe ich eine Methode auf, die ihrerseits ihre asynchrone Partnermethode aufruft und .Wait() zum Anruf hinzufügt.Unterschied zwischen dem Aufruf von .Wait() für eine asynchrone Methode und Task.Run(). Wait()

Diese Methode dann nach unten geht mehreren Ebenen (d.h., ruft eine andere Asynchron-Methode, die eine andere Async-Methode aufruft, und so weiter), und schließlich ruft eine Async Methode auf einem Objekt Httpclient. An diesem Punkt scheint der Faden in einem Kaninchenloch zu verschwinden; Die Methode ruft nie zurück.

Jetzt weiß ich, dass die asynchrone Reihe von Aufrufen wie erwartet funktioniert, da der gleiche Code auch von einem Web-API-Controller aufgerufen wird (die Controller-Methode ruft die asynchrone Version dieser ersten Methode auf, nicht die synchrone 'Partner'-Methode) , das vollständig async ist und wie erwartet zurückkehrt.

Also im Grunde habe ich so etwas wie diese, die nie

protected void btn_Click(object sender, EventArgs e) 
    > Class1.DoSomething() 
     > Class1.DoSomethingAsync.Wait() 
      ... 
       > await ClassN.Authenticate() 
        { 
        await myHttpClient.PostAsync() // never returns 
        } 

kehren versuchte ich habe .ConfigureAwait(false) an dieser ersten Asynchron-Methode verwendet, aber ohne Erfolg.

Ich habe auch das, was tut Rückkehr

Task<IHttpActionResult> MyWebApiMethod() 
    > await Class1.DoSomethingAsync() 
     ... 
      > await ClassN.Authenticate() 
       { 
       await myHttpClient.PostAsync() // does return 
       } 

ich gefunden habe, dass ich die erste Version Arbeit machen kann, wenn ich es wie folgt ändern:

protected void btn_Click(object sender, EventArgs e) 
    > Class1.DoSomething() 
     > Task.Run(async() => await Class1.DoSomethingAsync()).Wait() 
      ... 
       > await ClassN.Authenticate() 
        { 
        await myHttpClient.PostAsync() 
        } 

Aber ich Ich weiß nicht warum.

Kann mir jemand erklären den Unterschied zwischen

Class1.DoSomethingAsync.Wait() 

ruft und ruft

Task.Run(async() => await Class1.DoSomethingAsync()).Wait() 
+3

Warum verwenden Sie es nicht auf die richtige Weise? 'async void btn_Click' und' erwarten Class1.DoSomethingAsync() '? – user3185569

+1

Verwenden Sie 'Task.Run' nicht mit bereits asynchronen Methoden, dies ist Verschwendung von Threads. Ändern Sie einfach die Signatur von 'button_click' eventhandler als @ user3185569 vorgeschlagen – Fabio

+0

@ user3185569 - Ich hatte das nicht verwendet, weil mir nicht bewusst war, dass ich' _ync' auf serverseitigen WebForms-Ereignissen verwenden konnte. Danke, dass du mir das gezeigt hast. – awj

Antwort

5

ich erklären dieses Verhalten in meinem Blogbeitrag Don't Block on Asynchronous Code und meine MSDN-Artikel auf asynchronous best practices.

der Faden scheint in einem Kaninchenloch zu verschwinden; Die Methode ruft nie zurück.

Dies geschieht, weil einer der await s auf dem ASP.NET Kontext fortzusetzen versucht, aber das Anforderungsthread (mit, dass ASP.NET-Kontext) wartet blockiert für die Aufgabe abzuschließen. Dies ist der Grund für Ihren Deadlock.

Ich habe versucht, .ConfigureAwait (false) auf dieser ersten async-Methode, aber ohne Erfolg.

Um diese Blockade mit ConfigureAwait(false) zu vermeiden, muss es genannt jedenawait in jede Methode angewandt werden. So muss DoSomethingAsync es verwenden, für jeden await, jede Methode, die DoSomethingAsync Anrufe es für jeden await verwenden müssen (zum Beispiel Authenticate), jede Methode, die diese Methoden aufrufen, müssen sie verwenden für jeden await (z.B. PostAsync) usw.Beachten Sie, dass Sie am Ende hier auf den Bibliothekscode angewiesen sind, und in der Tat hat HttpClient einige von diesen in der Vergangenheit verpasst.

ich gefunden habe, dass ich die erste Version Arbeit machen kann, wenn ich es ändern [Task.Run verwenden].

Yup. Task.Run wird seinen Delegaten auf einem Thread-Pool-Thread ohne beliebigen Kontext ausführen. Aus diesem Grund gibt es kein Deadlock: Keines der await s versucht, den ASP.NET-Kontext fortzusetzen.

Warum verwenden Sie es nicht den richtigen Weg. async void btn_Click und warten auf Class1.DoSomethingAsync()?

Verwenden Sie nicht Task.Run mit bereits asynchronen Methoden, das ist Verschwendung von Threads. Ändern Sie einfach Unterschrift von button_click Eventhandler

Und hier ist der Recht Antwort: nicht blockieren auf asynchronem Code. Verwenden Sie einfach einen async void Event-Handler und verwenden Sie stattdessen await.

P.S. ASP.NET Core verfügt nicht mehr über einen ASP.NET-Kontext, sodass Sie so viel blockieren können, wie Sie möchten, ohne Angst vor Deadlocks zu haben. Aber das solltest du natürlich immer noch nicht, denn das ist ineffizient.

Verwandte Themen