【发布时间】:2017-03-25 06:22:07
【问题描述】:
我有一个连续异步方法,用于轮询资源和消息队列等。
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 绑定代码。此外,我有几个监视器在运行,但只有一些监视器死了。
我承认,到目前为止,我只看到这种情况发生在根本需要解决的情况下,但我仍然担心在边缘情况下仍然可能发生这种情况。
作为一种解决方法,我可能需要添加一个计时器并在 Monitor 似乎已经死机时重新启动它。
一些补充说明:
- 它没有抛出异常。挂钩 UnobservedTaskException 或包装在 try/catch 中可以确认这一点。
- 绝对不会卡在内部等待中。我添加了一个简单的标志来确认,它进入“等待 Task.Delay(100)”,但它再也没有回来。
问题
- 什么可能导致这种情况发生?
- 我将如何调试它?例如,我可以检查哪些对象以查看给定上下文中正在运行的异步方法的列表?
我怀疑 Visual Studio 2015“任务窗口”应该列出所有异步方法,但它是空白的。它说“没有要显示的任务”。我从未见过它显示任何内容。
更多信息:
我确定它没有引发异常或陷入无限期等待。症状似乎是任务不再运行(或延迟了非常长的时间)。它似乎还在继续运行一些任务,而实际上仍在运行的是较新的任务。
我有一个理论,在这种罕见的情况下,会优先考虑最近创建的任务。它将尽可能有限的时间分配给最近创建的那些,而旧的实际上不再运行。
如果我可以访问任务列表会有所帮助。然后我可以确认是否是这种情况。我可以为线程做到这一点,但到目前为止我还没有找到如何为任务做到这一点。
2016 年 6 月 7 日更新:
似乎有问题的任务实际上仍在运行。只是明显延迟了。例如,如果我运行三个运行此方法的任务,然后重新创建边缘情况 - 其中两个运行良好(等待 100 毫秒后恢复),但其中一个(最旧的)需要 2 秒到 20 秒(有时更多)。因此,调度程序似乎没有尝试公平地分配有限的处理可用性。
根据建议,我将分成两个明确说明的问题:
- 如何访问任务列表
- 调度程序的行为方式
部分答案
1.什么可能导致这种情况发生?
我无法了解调度程序如何管理任务,但观察表明,当调度程序落后时,生成的任务调度甚至不接近公平。一些任务可能会显着延迟,并且似乎确实偏向于较新的任务。例如,在一项测试中,三个相同的任务预计每 100 毫秒处理一次:
- 最近的两个仍然每隔约 150 毫秒持续运行一次
- 最旧的延迟从 500 毫秒到 30 秒不等
2.我将如何调试这个?例如,我可以检查哪些对象以查看给定上下文中正在运行的异步方法的列表?
我无法弄清楚如何使用对象检查来获取任务列表,但最简单的方法是获取任务列表或查看队列。 VS2015 中的“Parallel Stacks”或“Tasks”窗口是查看所有任务的好工具,但有局限性:
- 它没有列出来自 Windows 7 中异步方法的任务
- 它不会为您提供有关队列或优先级的任何信息。您可以随时添加自己的计时器来猜测它。
【问题讨论】:
-
您在什么上下文中运行代码?它是 WPF 应用程序还是 webb?你能在单元测试中重现错误吗?
-
另外,我注意到您没有等待 MonitorAsync。这将吞噬所有异常。看看这个问题stackoverflow.com/questions/15522900/…
-
“简化并发问题”——但看起来它正在导致严重的调试问题。我建议如果您想要后台活动,请使用
BackgroundWorker或显式启动线程并开始管理您的并发问题,而不是尝试通过创建多个任务来伪造并发它们都在竞争访问 UI 线程。 -
@smoksnes - 这是一个 WPF 应用程序。如描述中所述,它绝对不会引发异常,并且这种情况很容易覆盖,而无需将调用者更改为阻塞(实际上,对于关键监视器来说,UnobservedTaskException 就足够了)。也就是说,我确实喜欢 ContinueWith() 模式
-
@denis 该方法返回一个任务。你只是忽略它。它会告诉你它是否完成,以及导致它失败的任何错误。
标签: c# .net asynchronous async-await