2016-08-19 79 views
4

在「舊」時代,很容易跟蹤哪種方法掛起:只需進入調試器,點擊「暫停」按鈕並瀏覽堆棧跟蹤。如何找到哪個方法與異步/等待掛起?

現在,如果問題出在異步方法中,這種方法不起作用 - 因爲下一段要執行的代碼被埋在繼續任務的某處(從技術上說,它甚至不會掛起)......是一個簡單的調試任務的方法?

UPD。

例子:

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; 
    } 
} 

請注意,如果你一開始它並點擊「暫停」,你將只能看到DoHolyWar方法是掛,但你不會看到哪兒。而如果用.Wait()替換'await',並執行相同的操作,則可以檢查掛起的堆棧跟蹤。這個例子很簡單,但在真實世界的應用程序中,通常很難找到問題。特別是桌面應用程序將在主線程上運行事件循環,因此即使某些事情「掛起」,並且您點擊「暫停」,您也不會知道哪裏出了問題。

+0

作爲一般規則,它幾乎總是試圖通過顯式調用「Wait」或「Result」來嘗試行爲,就像異步不存在一樣。 –

+0

啊,好的。所以它不是一個真正的「掛起」,因爲你的程序仍然是響應式的(如果你已經完成了WPF或winforms程序並且執行了「等待DoHolyWar()」程序仍然會響應,你可能想要改變你的問題做而不是作爲一個控制檯應用程序來使它更清楚你不是在談論實際的「掛起」),而是發現'SetResult'永遠不會被調用。 –

+1

事實上,我更新了你的問題來做到這一點,如果你不喜歡它可以隨時回滾更改。 –

回答

1

在這樣的情況下,你所能做的就是去調試下拉菜單,轉到Windows,然後選擇「任務」窗口(默認快捷鍵組合是「按Ctrl + dķ 「)。

這可以給你一個關於掛什麼任務的線索。

enter image description here

尋找具有異常長Duration值的任務,這是一個跡象表明,事情發生的任務,它是沒有完成。如果雙擊該行,它會將您帶到掛起的等待中。

我不確定爲什麼await BurnHeretics();沒有出現在我的列表中,但我知道這個窗口的行爲會因操作系統版本而異,因爲它依賴於操作系統功能來跟蹤某些類型的任務。但至少這會告訴你,await DoHolyWarComplicatedDetails();掛起,這將導致你檢查DoHolyWarComplicatedDetails()這將導致你檢查BurnHeretics();這將導致你導致你的bug掛起。

UPDATE:我剛剛意識到它確實顯示await BurnHeretics();是主要的因素。如果您查看Task列,則<DoHolyWarComplicatedDetails>d__3意味着「在方法DoHolyWarComplicatedDetails中編譯器生成的類<DoHolyWarComplicatedDetails>d__3已計劃並正在等待信號到達。」 <DoHolyWarComplicatedDetails>d__3await BurnHeretics();的狀態機,如果你使用像DotPeek這樣的反編譯器並允許show編譯器生成代碼,你可以看到它。

[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

當然,如果你提供一個[MCVE](http://stackoverflow.com/help/mcve),你很難找到源代碼,我會很樂意更新我的答案來代替它,並告訴你如何跟蹤它下。 –

+0

更新了問題。 – ironic

+0

@ironic我已經更新了我的答案,我希望這可以幫助。 –