.NET Task 实例可以在运行期间超出范围吗?

Posted

技术标签:

【中文标题】.NET Task 实例可以在运行期间超出范围吗?【英文标题】:Can .NET Task instances go out of scope during run? 【发布时间】:2011-02-16 11:44:58 【问题描述】:

如果我在方法中有以下代码块(使用 .NET 4 和任务并行库):

var task = new Task(() => DoSomethingLongRunning());
task.Start();

然后方法返回,该任务会超出范围并被垃圾收集,还是会运行完成?我没有注意到 GCing 有任何问题,但我想确保我没有为 GC 设置竞争条件。

【问题讨论】:

一段时间后我发现了一个小事,我想我会和你分享,为了完整起见......请参阅下面的更新。 【参考方案1】:

更新:

在我回答了这个问题之后(很久以前!)我发现任务总是运行到完成是不正确的 - 有一个小的,比如说“角落”案例,任务可能无法完成。

原因是这样的:正如我之前回答的,任务本质上是线程;但它们是 background 线程。当所有前台线程完成时,后台线程会自动中止。因此,如果您对任务不做任何事情并且程序结束,则任务可能无法完成。

您应该始终等待任务。更多信息可以在excellent answer Jon gave me 上找到。


原文:

任务被调度到 ThreadPool,这意味着它们本质上是线程¹(实际上,它们封装了线程)。

来自Thread documentation:

没有必要保留 一旦你引用一个 Thread 对象 已经启动线程。线程 继续执行直到线程 程序完成。

所以,不,没有必要保留对它的引用。

另外,documentation 指出创建任务的首选方法是使用它的工厂:

您也可以使用 StartNew 方法 一次创建和启动一项任务 手术。这是首选方式 创建并启动任务(如果创建) 和调度不必是 分开(...)

希望对你有帮助。


¹根据documentation:

一个任务代表一个异步 操作,并且在某些方面它 类似于创建一个新线程 或 ThreadPool 工作项,但在 更高层次的抽象。

【讨论】:

任务可以调度到任何TaskScheduler,而不仅仅是线程池。【参考方案2】:

任务将运行完成。即使没有任何其他对它的引用(我相信没有生根是这个术语),线程池仍然会持有对它的引用,并至少防止它被垃圾收集(我至少说,因为即使完成后,不能保证它会被垃圾收集)直到完成。

【讨论】:

是的,rooted 是正确的术语。只要 something 具有对任务实例的有效(实时)引用,它就没有资格被收集。在这种情况下,线程池本身将持有该引用,直到线程完成。 更准确地说,TPL 的任务调度程序将在运行时引用该任务。如果没有那个引用,任务可能会被垃圾回收,但这不会阻止运行的代码完成。

以上是关于.NET Task 实例可以在运行期间超出范围吗?的主要内容,如果未能解决你的问题,请参考以下文章

C# -Task.Run() 中线程池中的索引超出范围异常

在获取数据期间索引超出部分中的行数范围

尝试重绘时“数组索引超出范围:1”

C# - 在for循环中使用相同的列表大小,索引超出了数组的范围[重复]

(opencv) 调试断言失败,向量下标超出范围

UWP 从 Task 更新 UI