我是否需要保留对“Task.Run”方法返回的“任务”的引用?

Do I need to keep a reference to the `Task` returned by `Task.Run` method?

如果我有异步方法:

async Task Process()
{
    while (condition)
    {
        await ...;
    }
}

而且我需要安排这个方法的执行。所以我使用:

Task.Run(Process);

我是否需要保留 Task.Run 方法返回的 Task 对象的引用以确保 Process 执行到完成?


这里有更多的上下文:我需要在我的应用程序中创建很多(大约 50K)任务队列。所以我想创建一个无线程队列处理设计,当队列为空时(除了内存之外)没有成本(因为大多数这些队列都是空的)。

示例要点 class:https://gist.github.com/hemant-jangid/e990b27507596c086e5651f504d0521f

一般来说,您不应该扔掉 Task。这不是关于确保任务会运行(它会1),而是关于观察异常。如果您不对 Task 执行 something,那么找出问题所在的唯一方法是添加一个 UnobservedTaskException 事件处理程序。不理想。

最好将 await 代码放在 try/catch 块中,或者注册一个 ContinueWith 处理程序,在您指定的地方应该调用错误.

不幸的是(IMO).NET 的行为在 4.5 左右发生了变化,因此未观察到的异常不再破坏进程。这意味着,如果您未能观察到它们,那么您的代码就会失败,并且 没有办法 tracing/logging 这种情况。


general 中,围绕一个已经产生 Task 的方法做 Task.Run 是不必要的,而且会适得其反。该方法已经承诺提供 Task - 为什么将其包装在第二个中? (该方法如何以及为何 returns Task 是该方法的实现细节,none 是调用者业务的实现细节)。只有当你调用的方法确实 significant CPU-bound 工作并且你(调用者)当前处于 "precious" 上下文时,你才应该真正这样做- 例如在 UI 线程上。


1假设整个过程存活时间足够长,等等

为此,我们将不得不更深入地研究任务。 Tasks本质上是线程,Task的起源是为线程池调度的。现在考虑这句话:

Tasks are essentially threads; but they are background threads. Background threads are automatically aborted when all foreground threads finish. So, if you don't do anything with the task and the program ends, there's a chance the task won't complete.

所以你可能会想,如果后台线程在所有前台线程完成时自动中止,则必须保留对 task 的引用,事实上,这就是为什么你应该始终如前所述等待任务在 this post answer.

但是 让我们深入研究 Thread documentation 并查看这一行:

It is not necessary to retain a reference to a Thread object once you have started the thread. The thread continues to execute until the thread procedure is complete.

这意味着,虽然上面提到后台线程会在所有前台线程完成时自动中止,但无需保留对它的引用。

事实上,根据文档,创建任务的正确方法是使用它的工厂:

You can also use the StartNew method to create and start a task in one operation. This is the preferred way to create and start tasks if creation and scheduling do not have to be separated