如何找到哪种方法在异步/等待中“挂起”? [英] How to find which method 'hangs' with async/await?

查看:108
本文介绍了如何找到哪种方法在异步/等待中“挂起”?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在旧时代,很容易跟踪挂起的方法:只需转到调试器,单击暂停按钮并遍历堆栈跟踪。

In the 'old' times it was very easy to track which method is hanging: just go to debugger, hit 'pause' button and go through stack traces.

但是,现在,如果问题出在异步方法中,则该方法不起作用-因为要执行的下一段代码被埋在延续任务中的某个位置(从技术上讲,它甚至都没有挂起)...有没有办法

Now however, if the problem is in the async method, this approach does not work - since the next piece of code to execute is buried somewhere in the continuation tasks (technically it does not even hang)... Is there a way for such easy debugging with tasks?

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方法正在挂起,但看不到确切的位置。如果您将.aWait()替换为 await,然后执行同样的操作,则可以检查挂起的堆栈跟踪。通过此示例,它非常简单,但是在实际应用中,通常很难发现问题。特别是,桌面应用程序将在主线程上运行事件循环,因此,即使某些事情挂起,而您单击暂停,您也不会知道出了什么问题。

Notice that if you start it and hit 'pause' you will only see that DoHolyWar method is hanging, but you will not see where exactly. While if you replace 'await's with .Wait(), and do the same you will be able to examine the hanging stack trace. With this example it is quite simple, but in a real-world application it is often quite hard to find the problem. In particular a desktop application will have events loop running on the main thread, so even if something does 'hang', and you hit 'pause' you will get no clue about what went wrong.

推荐答案

在这种情况下,您可以执行以下操作:转到调试下拉菜单,转到Windows,然后选择任务窗口(默认的快捷键组合是 Ctrl + D K )。

In situations like this, what you can do is go to the debug dropdown menu, go to Windows, and choose the "Tasks" window (The default short cut key combo is "Ctrl+D, K").

这可以为您提供有关挂起了哪些任务的线索。

This can give you a clue on what tasks are hanging.

查找异常长的任务持续时间值,表明任务发生了某些事情且尚未完成。如果您双击该行,它将带您进入挂起的等待状态。

Look for tasks that have abnormally long Duration values, that is a indication that something happened to the task and it is not completing. If you double click on the line it will take you to the await that is hung.

我不确定为什么等待BurnHeretics( ); 不会显示在我的列表中,但是我知道此窗口的行为将取决于您的OS版本,因为它依赖于OS功能来跟踪某些类型的任务。但这至少会告诉您正在等待DoHolyWarComplicatedDetails(); 正在挂起,这将导致您检查 DoHolyWarComplicatedDetails()这将导致您检查 BurnHeretics(); 这将导致您导致挂起的错误。

I don't know for sure why await BurnHeretics(); does not show up in the list for me, but I do know that this window will behave differently depending on your OS version because it relies on OS features to track some types of tasks. But at minimum this will show you that await DoHolyWarComplicatedDetails(); is hanging which will lead you to inspect DoHolyWarComplicatedDetails() which will lead you to inspect BurnHeretics(); which will lead you to your bug that is causing the hang.

更新:我只是意识到它确实显示 await BurnHeretics(); 是造成该块的主要原因。如果您查看 Task 列,则< DoHolyWarComplicatedDetails> d__3 表示在方法 DoHolyWarComplicatedDetails 编译器生成的类< DoHolyWarComplicatedDetails> d__3 已调度,正在等待信号到达。 < DoHolyWarComplicatedDetails> d__3 是用于 await BurnHeretics(); 的状态机,如果使用

UPDATE: I just realized it does show await BurnHeretics(); as the main thing causnig the block. If you look at the Task column, the <DoHolyWarComplicatedDetails>d__3 means "in the method DoHolyWarComplicatedDetails the compiler generated class <DoHolyWarComplicatedDetails>d__3 is scheduled and is waiting for a signal to arrive." the <DoHolyWarComplicatedDetails>d__3 is the state machine for await BurnHeretics();, you can see it if you use a decompiler like DotPeek and allow show compiler generated code.

[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)
  {
  }
}

这篇关于如何找到哪种方法在异步/等待中“挂起”?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆