在异步编程中悬空等待和可能的内存泄漏

Yur*_* S. 10 c# error-handling asynchronous async-ctp .net-4.5

由于各种原因,包含在.NET 4.5和Async CTP 4.0 中等待的流可能会卡住,例如,因为远程客户端没有响应.当然,WaitForAny,当我们等待一些超时任务时,是一个明显的解决方案,用于恢复高级流程.但是,这并没有解决所有可能的问题.

我有以下问题:

  1. 等待的环境会发生什么变化?据我所知,这会造成内存泄漏.我对吗?

  2. 如何检查调试器或使用相应的API 在应用程序中存在多少悬空" awaiter "?

  3. 是否可以在全球范围内枚举它们?

  4. 如果3.是正确的,是否可以强制取消这些*await*s 的任务(即清理)?

注意:在问题4中,我不询问在显式创建任务期间要使用的取消项目.我指的是间接创建任务的情况:

async Task<bool> SomeTask()
{
   await Something();
   ...
   return true;
}
Run Code Online (Sandbox Code Playgroud)

这个问题的动机:

  1. 试图避免内存泄漏
  2. 尝试使用涉及取消令牌的太多案例来复制代码
  3. 在许多情况下,每个低级别任务事先都不知道超时,但高级别流程可以只使用恢​​复方法:"我们被卡住了?没关系,只是清理并让我们重新开始".

Ste*_*ary 8

1等待的环境会发生什么变化?

我相信它会导致内存泄漏(如果你await正在进行I/O操作).最好总是完成你Task(这意味着你的async方法迟早会回来).

从svick的评论更新:有些情况下这不会导致内存泄漏.

2如何在调试器中或使用相应的API检查应用程序中存在多少悬空"awaiter"?

我不确定是否有一种简单的方法可以做到这一点.我相信应该可以编写一个调试器插件,它使用SoS来查找与编译器生成的异步状态机模式匹配的现有堆对象.

但是,这是一个很大的一点好处的工作.

3是否可以在全球范围内枚举它们?

不是普通的API.

如果3是正确的,是否可以强制取消这些等待的任务(即清理)?

即使您可以在运行时枚举它们(例如,通过分析API),也不能"强制"取消任务.取消是合作的.


处理此问题的正确方法是使用标准取消.基于任务的异步模式文档指定了可取消async方法的准则.

在最低级别:asyncBCL中的许多API都是可选的CancellationToken.

在中间层:让async方法采用可选方法并将其CancellationToken传递给其他async方法是很常见的.

在最高级别:在给定时间之后创建一个CancellationToken会触发的很容易.


lbo*_*zen 2

关于问题2和3我没有真正的答案。

  1. 我对此表示怀疑,只要当应用程序终止时 CLR 处置任务时,任务中使用的资源得到正确处置即可。

拥有一去不复返的任务绝不是一件好事。为了避免这种情况并回答第 4 点:任务可以取消。

您需要创建一个传递给任务的取消令牌。该任务自行负责监视取消令牌的状态,并在取消时抛出异常。(也可以使用相同的令牌一次取消多个任务。)

MSDN 上的这篇文章向您展示了如何执行此操作。