2016-08-19 4 views
4

In den "alten" Zeiten war es sehr einfach zu verfolgen, welche Methode hängt: Gehen Sie einfach zum Debugger, klicken Sie auf "Pause" und gehen Sie durch die Stack-Traces.Wie finde ich heraus, welche Methode 'hängt' mit async/abwarten?

Jetzt, wenn das Problem in der asynchronen Methode ist, funktioniert dieser Ansatz nicht - da der nächste Stück Code ausgeführt wird irgendwo in den Fortsetzungsaufgaben begraben (technisch hängt es nicht einmal) ... Gibt es ein Weg für so einfaches Debuggen mit Aufgaben?

UPD.

Beispiel:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent();   
    } 

    private async void MainWindow_OnLoaded(object sender, RoutedEventArgs e) 
    { 
     await DoHolyWar(); 
     MessageBox.Show("Holy war complete!"); 
    } 

    public static async Task DoHolyWar() 
    { 
     await DoHolyWarComplicatedDetails(); 
     Console.WriteLine("Victory!"); 
    } 

    public static async Task DoHolyWarComplicatedDetails() 
    { 
     await BurnHeretics(); 
    } 

    public static Task BurnHeretics() 
    { 
     var tcs = new TaskCompletionSource<object>(); 

     // we should have done this, but we forgot 
     // tcs.SetResult(null); 

     return tcs.Task; 
    } 
} 

Beachten Sie, dass, wenn Sie es starten und drücken Sie ‚Pause‘ Sie werden sehen, dass DoHolyWar Methode hängt, aber Sie werden nicht, wo genau sehen. Wenn Sie 'wait' mit '.Wait()' ersetzen und dasselbe tun, können Sie die hängende Stack-Spur untersuchen. Mit diesem Beispiel ist es ziemlich einfach, aber in einer realen Anwendung ist es oft ziemlich schwierig, das Problem zu finden. Insbesondere bei einer Desktop-Anwendung wird die Ereignisschleife im Hauptthread ausgeführt. Wenn also etwas "hängt" und Sie auf "Pause" klicken, werden Sie keine Ahnung haben, was falsch gelaufen ist.

+0

Als allgemeine Regel gilt, dann ist es fast immer derjenige, der als ob Asynchron durch expliziten Aufruf 'Wait' oder' Result' existiert nicht zu handeln versucht. –

+0

Ah, ok. Es ist also nicht wirklich ein "Hang", da Ihr Programm immer noch reaktiv ist (wenn Sie ein WPF- oder Winforms-Programm gemacht hätten und "DoHolyWar()" erwartet hätten, würde das Programm immer noch reagieren. Vielleicht möchten Sie Ihre Frage ändern Anstatt eine Konsolen-App zu sein, um deutlicher zu machen, dass Sie nicht von einem tatsächlichen "Hang" sprechen, wird festgestellt, dass ein 'SetResult' nie aufgerufen wird. –

+1

In der Tat habe ich Ihre Frage aktualisiert, um dies zu tun, wenn Sie es nicht mögen, fühlen Sie sich frei, um die Änderung rückgängig zu machen. –

Antwort

1

In Situationen wie dieser, was Sie tun können, ist auf die Debug-Drop-Down-Menü gehen, gehen auf Windows, und wählen Sie die „Tasks“ Fenster (Der Standard kurz geschnitten Tastenkombination ist "Strg + D, K ").

Dies kann Ihnen einen Hinweis darauf geben, welche Aufgaben hängen.

enter image description here

Geben Sie für Aufgaben, die ungewöhnlich lange Duration Werte haben, ist, dass ein Hinweis darauf, dass etwas an die Aufgabe passiert ist, und es ist die Vollendung nicht. Wenn Sie auf die Linie doppelklicken, werden Sie zu dem Warten kommen, das hängt.

Ich weiß nicht genau, warum await BurnHeretics(); nicht in der Liste für mich angezeigt wird, aber ich weiß, dass dieses Fenster je nach Betriebssystemversion anders verhält, da es auf Betriebssystemfunktionen angewiesen ist, um einige Arten von Aufgaben zu verfolgen . Aber zumindest wird dies zeigen, dass await DoHolyWarComplicatedDetails(); hängt, die Sie zur Inspektion DoHolyWarComplicatedDetails() führt, die Sie zur Überprüfung BurnHeretics(); führen wird, die Sie zu Ihrem Fehler führen wird, der den Hang verursacht.

UPDATE: Ich habe gerade festgestellt, es zeigt await BurnHeretics(); als die Hauptsache causnig den Block. Wenn Sie die Spalte Task betrachten, bedeutet die <DoHolyWarComplicatedDetails>d__3 "in der Methode DoHolyWarComplicatedDetails der Compiler generierte Klasse <DoHolyWarComplicatedDetails>d__3 ist geplant und wartet auf ein Signal ankommen." Die <DoHolyWarComplicatedDetails>d__3 ist die Zustandsmaschine für await BurnHeretics();, Sie können es sehen, wenn Sie einen Decompiler wie DotPeek verwenden und Show Compiler generierten Code zulassen.

[CompilerGenerated] 
private sealed class <DoHolyWarComplicatedDetails>d__3 : IAsyncStateMachine 
{ 
    public int <>1__state; 
    public AsyncTaskMethodBuilder <>t__builder; 
    private TaskAwaiter <>u__1; 

    public <DoHolyWarComplicatedDetails>d__3() 
    { 
    base..ctor(); 
    } 

    void IAsyncStateMachine.MoveNext() 
    { 
    int num1 = this.<>1__state; 
    try 
    { 
     TaskAwaiter awaiter; 
     int num2; 
     if (num1 != 0) 
     { 
     awaiter = MainWindow.BurnHeretics().GetAwaiter(); 
     if (!awaiter.IsCompleted) 
     { 
      this.<>1__state = num2 = 0; 
      this.<>u__1 = awaiter; 
      MainWindow.<DoHolyWarComplicatedDetails>d__3 stateMachine = this; 
      this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, MainWindow.<DoHolyWarComplicatedDetails>d__3>(ref awaiter, ref stateMachine); 
      return; 
     } 
     } 
     else 
     { 
     awaiter = this.<>u__1; 
     this.<>u__1 = new TaskAwaiter(); 
     this.<>1__state = num2 = -1; 
     } 
     awaiter.GetResult(); 
     awaiter = new TaskAwaiter(); 
     Console.WriteLine("Heretics burned"); 
    } 
    catch (Exception ex) 
    { 
     this.<>1__state = -2; 
     this.<>t__builder.SetException(ex); 
     return; 
    } 
    this.<>1__state = -2; 
    this.<>t__builder.SetResult(); 
    } 

    [DebuggerHidden] 
    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) 
    { 
    } 
} 
+0

Sicher, wenn Sie eine [MCVE] (http://stackoverflow.com/help/mcve) zur Verfügung stellen, die Sie schwer finden, die Quelle zu finden, werde ich glücklich meine Antwort aktualisieren, um stattdessen diesen Code zu verwenden und Ihnen zeigen, wie Sie es verfolgen Nieder. –

+0

aktualisierte die Frage. – ironic

+0

@ironic Ich habe meine Antwort aktualisiert, ich hoffe, das hilft. –

Verwandte Themen