【问题标题】:Create a completed Task创建一个已完成的任务
【发布时间】:2012-12-24 00:36:29
【问题描述】:

我想创建一个完整的Task(不是Task<T>)。 .NET 中是否有内置功能可以做到这一点?

一个相关的问题: Create a completed Task<T>

【问题讨论】:

  • It seems like the answer I'm getting from everyone is that using a garbage value like this is the correct way. That there isn't a way to do this without the garbage value is disappointing -- oh well. 你觉得这有什么问题?如果你缓存一个Task,那么你的整个程序会占用额外的内存。那是没什么。此外,如果不这样做,也可以创建一个已完成的任务,这不会更好。
  • 哦,我的失望与不得不使用额外的内存无关。只是代码中任何地方的垃圾值都不优雅。
  • 请注意,今天有 ValueTask 用于已完成的任务(即对于您已经拥有的值,因此代码基本上是同步的),这将为您节省分配。

标签: c# .net async-await task-parallel-library


【解决方案1】:

您可以使用来自伟大图书馆的Nito.AsyncEx.TaskConstants.Completed AsyncEx 来自Stephen Cleary

【讨论】:

  • Nito.AsyncEx.TaskConstants.Completed 源文件的链接已失效
  • @harlam357 已修复。
【解决方案2】:

对于.Net 4.6 及更高版本使用

return Task.CompletedTask;

低版本可以使用

return new Task(() => { });

【讨论】:

  • 对于 4.6 之前的方法,小心!您需要启动任务,否则它永远不会完成!改用return Task.Delay(0); 怎么样?
【解决方案3】:

我首选的方法是不带参数地调用Task.WhenAll()MSDN documentation 声明“如果提供的数组/可枚举不包含任务,则返回的任务将在返回给调用者之前立即转换为 RanToCompletion 状态。”。这听起来像你想要的。

更新:我在Microsoft's Reference Source 找到了源代码;在那里你可以看到 Task.WhenAll 包含以下内容:

return (tasks.Length == 0) ? // take shortcut if there are no tasks upon which to wait
            Task.CompletedTask :
            new WhenAllPromise(tasks);

所以 Task.CompletedTask 确实是内部的,但它是通过调用不带参数的 WhenAll() 暴露出来的。

【讨论】:

  • 虽然感觉有点老套,但它得到了文档的支持,所以我不知道,我有点喜欢它!
  • 对于我和我的 .NET 4.5.2 受限代码来说,它已经足够优雅了。谢谢!
【解决方案4】:

newest version of .Net (v4.6) 只是添加了一个内置的Task.CompletedTask

Task completedTask = Task.CompletedTask;

该属性被实现为无锁单例,因此您几乎总是使用相同的已完成任务。

【讨论】:

  • 刚查了最新的VS 14 CTP,创建了一个4.5.3的项目,Task.CompletedTask还在内部。
  • 2.要么你没有下载最新的 CTP(它是 4 并且从那个站点链接到)或者你没有指定版本 4.5.3。 Here's what's on my machine。 @彼得里奇
  • 我从 Azure 上的 Visual Studio 14 映像创建了一个 VM。
  • 我正在使用单声道 5.4.1.7 的 Mac OS X 工作,我得到了同样的错误。我该如何解决这个问题?
【解决方案5】:

怎么样:

#pragma warning disable 1998
    public async Task emptyTask() {
    }
#pragma warning restore 1998

如果您不介意,可以省略警告抑制。

【讨论】:

  • 我相信标记一个方法async即使你不使用它仍然会创建整个状态机设备,因此返回一个空任务对于低资源场景更有效。
【解决方案6】:

我会使用Task.Delay(0)。在内部,它返回一个已完成的Task&lt;T&gt; 的缓存实例。无论如何,这正是当前答案建议做的事情,只是现在您不必自己缓存实例,代码中也没有任何不雅的垃圾值。

您可能认为可以改用Task.Yield(),但事实证明Task.Yield() 的结果不是Task 的子类型,而Task.Delay(0) 的结果是。这是两者之间的细微差别之一。

【讨论】:

    【解决方案7】:

    Task&lt;T&gt; 可以隐式转换为Task,所以只需获得一个完整的Task&lt;T&gt;(带有任何T 和任何值)并使用它。您可以使用类似的方法来隐藏实际结果在某处的事实。

    private static Task completedTask = Task.FromResult(false);
    public static Task CompletedTask()
    {
        return completedTask;
    }
    

    请注意,由于我们不公开结果,并且任务始终完成,我们可以缓存单个任务并重用它。

    如果您使用的是 .NET 4.0 并且没有 FromResult,那么您可以使用 TaskCompletionSource 创建自己的:

    public static Task<T> FromResult<T>(T value)
    {
        var tcs = new TaskCompletionSource<T>();
        tcs.SetResult(value);
        return tcs.Task;
    }
    

    【讨论】:

    • 如果您使用的是 4.0,您可以添加 Microsoft.Bcl.Async 库来获取 TaskEx.FromResult,以及其他方便的东西,例如 WhenAll
    • @Servy FromResult(false) 和 FromResult(true) 有什么变化?
    • @FrancescoB。如果您曾经查看结果,那么它会更改结果的布尔值。如果你忽略了结果,那么结果是什么就无关紧要了。
    【解决方案8】:

    您可以使用Task.FromResult(在.NET 4.5 中)返回一个完整的Task&lt;T&gt;

    如果您需要非泛型 Task,则始终可以使用 Task.FromResult(0) 或类似名称,因为 Task&lt;T&gt;Task 的子类。

    【讨论】:

      猜你喜欢
      • 2011-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多