【问题标题】:What would cause a method marked with async to execute synchronously, even though it contains an await call?什么会导致标记为 async 的方法同步执行,即使它包含等待调用?
【发布时间】:2012-01-06 22:41:43
【问题描述】:

我正在尝试调试我的同事不在时编写的一些代码。

来电:

var sw = Stopwatch.StartNew();
logger.Trace("Time A: {0}ms", sw.ElapsedMilliseconds); // Roughly 0ms
pt.Task = ProcessMetricsWorker(pt.CancelTokenSource);
sw.Stop();
logger.Trace("Time B: {0}ms", sw.ElapsedMilliseconds); // Over 20000ms!

这是方法签名:

async Task ProcessMetricsWorker(CancellationTokenSource cancelTokenSource)

该方法运行大约需要 20 秒,根据我在上面引用的第一行之前和之后放置的日志记录,该任务正在同步执行,就好像 async 部分被忽略了一样。什么会导致这种情况发生?

请注意,异步方法中有一个await 调用。它在一个 while 循环中,但我无法想象这会有所作为。

// internal to the method referenced above
result = await metricProcessor.ProcessItem(domain);

另外,我创建了一个小型测试应用程序,它运行一个非常简单的 async/await 设置,以确保 await 在我正在运行它的服务器上实际工作,并且它在我的小测试用例中正常工作,只是没有在我尝试调试的主应用程序中。

【问题讨论】:

    标签: c# async-ctp


    【解决方案1】:

    每个async 方法都作为常规方法调用启动。它只会在第一次对可等待对象(尚未完成)执行await 时变为异步(在让步于其调用者的意义上)。

    所以,ProcessMetricsWorker 中发生的事情有几种可能性:

    • 第一个await之前可能包含一些长时间运行的同步代码。
    • awaits 的许多第一个等待对象可能已经完成。如果它们全部已经完成,那么ProcessMetricsWorker实际上是同步的。

    “快速路径”blog post 包含有关如何将await 转换为代码的更多详细信息,并显示了它仅在作用于未完成的等待对象时如何产生。

    【讨论】:

      【解决方案2】:

      好吧,我想我在 Async CTP 中发现了一个错误。

      我添加了这个方法:

      async Task DummyTask()
      {
          await TaskEx.Run(() => Thread.Sleep(1000));
      }
      

      然后在ProcessMetricsWorker 方法中添加这个:

      await DummyTask();
      

      嘿,现在,该方法是异步的!我想知道编译器是否以某种方式忽略了方法中的内部等待调用,而这一切在 MSIL 代码中都是同步的......

      【讨论】:

      • 我猜你把它放在函数的开头。如果是这种情况,则表明您的函数在第一次等待之前只是做了很多工作。
      猜你喜欢
      • 1970-01-01
      • 2022-11-19
      • 2021-11-28
      • 1970-01-01
      • 2016-02-24
      • 2016-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多