.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 实例可以在运行期间超出范围吗?的主要内容,如果未能解决你的问题,请参考以下文章