C# 中 Task.FromResult<TResult> 的用途是啥

Posted

技术标签:

【中文标题】C# 中 Task.FromResult<TResult> 的用途是啥【英文标题】:What is the use for Task.FromResult<TResult> in C#C# 中 Task.FromResult<TResult> 的用途是什么 【发布时间】:2013-11-03 07:10:36 【问题描述】:

在 C# 和 TPL (Task Parallel Library) 中,Task 类表示正在进行的工作,它会产生 T 类型的值。

我想知道Task.FromResult方法有什么需要?

也就是说:在你手头已经有了产生的价值的情况下,有什么需要将其包装回任务中?

唯一想到的是,它被用作接受 Task 实例的其他方法的适配器。

【问题讨论】:

这对您有帮助吗? msdn.microsoft.com/en-us/library/hh228607.aspx 在某种程度上我同意这一点,但是像这样创建密集的、有用的、统一的、面向讨论的页面是一个巨大的好处。我几乎总是从一个好的、密集的 *** 页面中学到更多,而不是从谷歌搜索和跨多个地方进行研究,所以在这种情况下,我真的很高兴他发布了这个。 我认为 Google 将我带到了 SO 并且 SO 让我去 Google。这是一个循环引用:) 【参考方案1】:

我发现了两个常见的用例:

    当您实现一个允许异步调用者的接口,但您的实现是同步的。 当您为测试存根/模拟异步代码时。

【讨论】:

#1 的一个好案例是 Web 服务。您可以有一个返回Task.FromResult 的同步服务方法和一个异步等待网络I/O 的客户端。这样您就可以使用ChannelFactory 在客户端/服务器之间共享相同的接口。 例如 ChallengeAsync 方法。 WTF 是 MS 的设计师在想什么?此方法绝对没有理由返回任务。 MS 的所有示例代码都只有 FromResult(0)。希望编译器足够聪明,可以将其优化掉,并且实际上不会产生一个新线程然后立即杀死它! @JohnHenckel:OWIN 从一开始就设计为对异步友好的。接口和基类经常使用异步签名,因为它只是允许(而不是强制)实现是异步的。所以它类似于从IDisposable 派生的IEnumerable&lt;T&gt; - 它允许 可枚举拥有一次性资源,而不是强制 它。 FromResultasyncawait 都不会产生线程。 @StephenCleary hmhm,感谢您的解释。我曾认为 await 会产生,但我试过了,我发现它没有。只有 Task.Run 可以。因此,x = await Task.FromResult(0);相当于说 x = 0;这很令人困惑,但很高兴知道! @OlegI:对于 I/O 操作,最好的 解决方案是异步实现它,但有时您别无选择。此外,有时您可以同步实现它(例如,缓存结果,如果值未缓存,则回退到异步实现)。更一般地说,Task-returning 方法意味着“可能是异步的”。因此,有时方法会被赋予一个异步签名,完全知道某些实现将是同步的(例如,NetworkStream 应该是异步的,但 MemoryStream 应该是同步的)。【参考方案2】:

一个例子是使用缓存的方法。如果结果已经计算出来,你可以返回一个带有值的已完成任务(使用Task.FromResult)。如果不是,那么您继续并返回一个代表正在进行的工作的任务。

缓存示例:Cache Example using Task.FromResult for Pre-computed values

【讨论】:

可以缓存已完成的任务,例如从Task.FromResult返回的任务。 @Paulo:将整个 Task 对象保存在内存中似乎比只缓存结果更浪费。 预期“价值任务”已被缓存。我不记得具体是哪些,但我认为 Task.FromResult(0)Task.FromResult(1)Task.FromResult(false)Task.FromResult(true) 被缓存了。您不应该为网络访问缓存任务,但结果中的一个任务非常好。您是否愿意在每次需要返回值时创建一个? ... 并回答我自己的问题,缓存 Tasks 的好处是其中一些可以完成任务,而另一些可以是尚未完成的任务还没完成。调用者不必关心:他们进行异步调用,如果已经完成,则在等待时立即得到答案,如果没有,则稍后再获得。如果没有这些缓存的任务,要么 (a) 需要两种不同的机制,一种是同步的,一种是异步的——这对调用者来说很麻烦,或者 (b) 必须动态创建一个任务,每次调用者要求一个已经可用的答案时(如果我们只缓存了一个 TResult)。 我现在明白了。答案的措辞有些混乱。任务本身没有内置的“缓存”机制。但是,如果您为 ... 说 .... 下载文件编写了一个缓存机制,Task GetFileAync( ),您可以使用 Task.FromResult(cachedFile) 立即返回一个已经在缓存中的文件,然后等待将同步运行,通过没有线程切换来节省时间。【参考方案3】:

当你想创建一个可等待的方法而不使用 async 关键字时使用它。 我找到了这个例子:

public class TextResult : IHttpActionResult

    string _value;
    HttpRequestMessage _request;

    public TextResult(string value, HttpRequestMessage request)
    
        _value = value;
        _request = request;
    
    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    
        var response = new HttpResponseMessage()
        
            Content = new StringContent(_value),
            RequestMessage = _request
        ;
        return Task.FromResult(response);
    

在这里,您正在创建自己的 IHttpActionResult 接口实现,以在 Web Api 操作中使用。 ExecuteAsync 方法应该是异步的,但您不必使用 async 关键字来使其异步且可等待。由于您已经有了结果并且不需要等待任何东西,因此最好使用 Task.FromResult。

【讨论】:

【参考方案4】:

来自 MSDN:

当您执行返回 Task 对象的异步操作并且该 Task 对象的结果已经计算出来时,此方法很有用。

http://msdn.microsoft.com/en-us/library/hh228607.aspx

【讨论】:

【参考方案5】:

当您想要进行异步操作但有时结果同步在手时,请使用 Task.FromResult。你可以在这里找到一个很好的样本http://msdn.microsoft.com/en-us/library/hh228607.aspx。

【讨论】:

在你的好样例中,结果不是同步在手,操作都是异步的,Task.FromResult用来获取之前缓存的异步结果。【参考方案6】:

我认为您可以将 Task.FromResult 用于需要很长时间才能完成的同步方法,而您可以在代码中执行其他独立工作。我宁愿让这些方法调用异步。但是想象一下您无法控制调用的代码并且您想要隐式并行处理的情况。

【讨论】:

不确定为什么 SO 排序会将这个答案放在列表的顶部,但它是不正确的。通过调用await Task.FromResult(...) 没有隐式并行处理(请参阅this question 以获得解释)。而是同步运行。如果您希望转换一个长时间运行的同步方法,使其可以与另一个并行调用,您应该改用await Task.Run(() =&gt; LongRunningTask());

以上是关于C# 中 Task.FromResult<TResult> 的用途是啥的主要内容,如果未能解决你的问题,请参考以下文章

对于代表返回 void 的操作的任务,Task.FromResult<T>() 的替代方法是啥?

如何使用Task.FromResult?

我应该使用 Task.Run 还是 Task.FromResult?

Task FromResult vs TaskCompletionSource SetResult

C#使用异步操作时的注意要点(翻译)

C#中异步方法的返回类型