为什么异步连续方法会停止运行?

时间:2016-06-02 05:34:52

标签: c# .net asynchronous async-await

我有一个连续的异步方法,用于轮询资源和消息队列等事情。

private async Task MonitorAsync(CancellationToken cancelToken)
{
  while (!cancelToken.IsCancellationRequested)
  {
    await Task.Delay(100);

    // Poll stuff and take action
  }
}

这是在UIContext中运行的(简化了并发问题)。

AppViewModel()
{
  MonitorAsync(); // Starts the Monitor
}

我观察到的是在某些极端条件下,这种异步方法将停止运行(例如,应用程序停止处理消息)。例如,如果在UIContext中运行了太多的CPU绑定代码。另外,我有一些监视器在运行,只有一些监视器死了。

我会批准到目前为止我只看到这种情况发生在一个从根本上需要解决的情况中,但我仍然担心在边缘情况下它仍然是可能的。

作为一种解决方法,我可能需要添加一个计时器并重新启动监视器,如果它似乎已经死亡。

一些补充说明:

  • 这不是一个例外。挂钩到UnobservedTaskException或在try / catch中包装确认了这一点。
  • 绝对不会停留在内部等待中。我添加了一个简单的标志来确认,它会“等待Task.Delay(100)”,但它永远不会回来。

问题

  1. 可能导致这种情况发生的原因是什么?
  2. 我将如何调试此操作?例如,我可以检查什么对象,查看给定上下文中运行异步方法的列表?
  3. 我怀疑Visual Studio 2015“任务窗口”应列出所有异步方法,但它是空白的。它说“没有要显示的任务”。我从未见过它显示任何东西。

    更多信息:

    我确信它没有抛出异常或陷入无限期等待。症状显示任务不再运行(或延迟很长时间)。它似乎也继续运行一些任务,实际上仍在运行的是新的任务。

    我有一个理论认为,在这种罕见的情况下,优先考虑最近创建的任务。它为最近创建的时间提供了有限的时间,旧的有效时间不再运行。

    如果我可以访问任务列表会有所帮助。然后我可以确认是否是这种情况。我可以为Threads做到这一点,但到目前为止我还没有找到如何为任务做到这一点。

    2016年6月6日更新:

    似乎有问题的任务实际上仍然在运行。它只是显着延迟。例如,如果我运行三个任务运行此方法,然后重新创建边缘情况 - 其中两个正常运行(等待100毫秒后恢复),但其中一个(最旧的一个)需要2秒到20秒(有时更多)。因此,调度程序似乎不会尝试公平地分配有限的处理可用性。

    根据建议,我将分成两个明确陈述的问题:

    1. 如何访问任务列表
    2. 调度程序的行为方式
    3. PARTIAL ANSWER

      1。可能导致这种情况发生的原因是什么?

      我无法了解调度程序如何管理任务,但观察显示,当调度程序落后时,生成的任务调度甚至不接近公平。某些任务可能会显着延迟,而且对于较新的任务似乎存在偏见。例如,在一个具有三个相同任务的测试中,期望每100毫秒进行一次处理:

      • 最近两次仍然每隔约150毫秒仍在运行
      • 最老的一个延迟了500毫秒到30秒

      2。我该如何调试呢?例如,我可以检查哪个对象,查看给定上下文中运行异步方法的列表?

      我无法弄清楚如何使用对象检查来获取任务列表,但却是获取任务列表或查看队列的最简单方法。 VS2015中的“并行堆栈”或“任务”窗口是查看所有任务的绝佳工具,但有限:

      • 它不会列出Windows 7中异步方法的任务
      • 它不会向您提供有关队列或优先级的任何信息。您可以随时添加自己的计时器来猜测它。

2 个答案:

答案 0 :(得分:1)

如果你没有等待任务,从不在上面调用Wait()ResultGetAwaiter()ContinueWith(),你怎么知道它甚至可以在收集垃圾之前的任何地方进行安排?

更改此行:

<击>

<击>
MonitorAsync(); // starts the Monitor

<击>

到此

<击>

<击>
MonitorAsync().ContinueWith(t => Console.WriteLine("monitor ended")); // starts the Monitor

<击>

使它开始运行?

编辑:这可能不是问题所在 - according to MSDN documentation&#34;当调用异步方法时,它会同步执行函数体,直到第一个await表达式在一个尚未等待的实例上尚未完成,此时调用将返回给调用者。&#34;,即无论你使用Task做什么,当然一直到第一个await点或结束时方法运行。我将此与使用Task创建new Task(...)进行混淆,然后不在任务计划程序上进行计划。 await内部的Task.Delay应足以将该方法的其余部分注册为续,并确保它将被执行。

答案 1 :(得分:0)

我认为只有两种选择:

  1. 该方法抛出异常。您已经说过它没有,但是也许您可以通过在调试器下运行应用程序来仔细检查,同时为所有CLR异常启用“中断时”?
  2. UI线程非常繁忙,它应该在恢复时永远无法运行您的方法。因为这也意味着用户界面会停止响应而你没有说它确实存在,这对我来说听起来不太可能。