【问题标题】:Should methods returning Task<T> always start the returned task?返回 Task<T> 的方法是否应该总是启动返回的任务?
【发布时间】:2012-07-29 04:54:56
【问题描述】:

如果我有类似的方法

Task<bool> LongProcessTaskAsync();

返回已启动的任务会更好吗

return Task<bool>.Factory.StartNew(() => { ... });

或者只是return new Task&lt;bool&gt;(() =&gt; ...)

就个人而言,我更喜欢第一种方法,但我更愿意与其他 API 和库保持一致。

返回未开始的任务是否更合适?

【问题讨论】:

    标签: c# task-parallel-library


    【解决方案1】:

    对于 async/await 方法,Task 已经启动。 AFAIK,为基于任务的版本添加的所有 BCL 方法都返回已启动的任务。不这样做会有点奇怪,因为现在常见的消费者案例是:

    var foo = await GetFooAsync();
    

    [编辑]基于 Stephen 指出 TAP 指南涵盖了这一点(他已经包含了指南的链接),我将引用第 4 页的相关位(在基于任务的异步模式定义 -> 行为 -> 任务状态),我在关键部分周围添加了粗体+斜体。

    任务状态

    Task 类为异步操作提供生命周期,并且 该循环由 TaskStatus 枚举表示。为了 支持派生自 Task 和 Task 的类型的极端情况 以及构造与调度的分离,Task 类 公开一个 Start 方法。由其公共构造函数创建的任务是 被称为“冷”任务,因为它们的生命周期开始于 非计划TaskStatus.Created状态,直到Start 在这些实例上被调用,它们进展到被调度。 所有其他任务都以“热”状态开始其生命周期,这意味着 它们所代表的异步操作已经启动 并且它们的 TaskStatus 是 Created 以外的枚举值。

    从 TAP 方法返回的所有任务都必须是“热的”。 如果 TAP 方法 在内部使用 Task 的构造函数来实例化要成为的任务 返回时,TAP 方法必须在 Task 对象上调用 Start 之前 退回它。 TAP 方法的使用者可以放心地假设 返回的任务是“热的”,不应尝试在任何 从 TAP 方法返回的任务。在“热”任务上调用 Start 将 导致 InvalidOperationException(此检查已处理 由 Task 类自动执行)。

    【讨论】:

    • +1。应该启动返回的TaskTask-Based Asynchronous Pattern guidelines 状态。任何使用您的方法的 async 代码都希望它能够启动,特别是如果它遵循 TAP 命名准则(即以 Async 结尾)。
    • 未启动的任务实际上仅由任务并行库代码使用。还有其他从 TPL 继承的 Task API 不应在 TAP 代码中使用:Task.WaitTask.WaitAllTask.WaitAnyTask.Result - 基本上是任何处理阻塞的东西。
    • @StephenCleary 假设我在一些异步代码的第一行调用var myTask = DoWorkAsync();(没有等待),然后我调用其他一些代码,例如var myOtherTask = DoMoreWorkAsync();。然后我做一些其他的事情,现在在结束我的方法之前,我需要确保这两个任务(异步启动)已经完成。我会使用Task.Wait(myTask, myOtherTask);await myTask; await myOtherTask;。你的意思是不应该使用我的第一个选项,而应该使用await(和ConfigureAwait(false)),还是我误解了?
    • @Alisson:如果它在方法的末尾,我会使用await Task.WhenAll(myTask, myOtherTask).ConfigureAwait(false)
    【解决方案2】:

    James Manning 正确回答。这是另一个角度:为什么有人想要未开始的任务?如果他这样做了,他本可以等待调用该方法。他本可以稍后调用它,或者自己将其包裹在 Lazy 或 future 中。几乎没有理由不返回已开始的任务。

    【讨论】:

    • 未启动的Task 可以在任何上下文中执行。所以 - 理论上 - 您可以使用未启动的 Task 作为执行上下文无关紧要的工作单元(即,TaskScheduler 留给调用者)。但是,我从未见过有人在实践中这样做。
    • 好吧,没想到。可能最好将 TaskScheduler 传递给被调用的方法,以便它可以决定它是否真的想要在不同的 TaskScheduler 上执行。
    猜你喜欢
    • 2016-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-12
    • 2017-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多