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


189

在C#和TPL(任务并行库)中,Task该类表示正在进行的工作,该工作产生类型T的值。

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

也就是说:在您已经掌握了生产价值的情况下,将其包装回Task的需求是什么?

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



30
在某种程度上,我同意这一点,但是像这样创建密集,有用,合并,面向讨论的页面是一个巨大的好处。我几乎总是从一个良好的,密集的stackoverflow页面中学到更多,而不是从谷歌搜索和跨多个地方进行研究中学到更多,因此,在这种情况下,我真的很高兴他发表了这篇文章。
Alex Edelstein

41
我认为Google带我去了,所以请我去Google。这是一个循环引用:)
gmail用户

Answers:


257

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

  1. 当您实现允许异步调用方的接口,但是您的实现是同步的。
  2. 存根/模拟异步代码以进行测试时。

6
#1的一个很好的例子是Web服务。您可能有一个返回的同步服务方法,Task.FromResult并且一个客户端正在等待网络I / O。这样,您可以使用来在客户端/服务器之间共享相同的接口ChannelFactory
纳尔逊·罗瑟梅尔

2
例如,ChallengeAsync方法。WTF是MS的设计师在想什么?此方法绝对没有理由返回Task。MS的所有示例代码都具有FromResult(0)。希望编译器足够聪明,可以对其进行优化,并且实际上不会产生新线程然后立即将其杀死!
约翰·亨克尔

14
@JohnHenckel:OWIN从头开始设计为异步友好的。接口和基类通常使用异步签名,因为它仅允许(而不是强制)实现异步。因此,它类似于IEnumerable<T>衍生自IDisposable-它允许枚举对象拥有一次性资源,而不是强制使用它。无论FromResultasync也不await将产生线程。
Stephen Cleary 2015年

4
@StephenCleary hmhm,感谢您的解释。我本来以为会产生等待,但我尝试了一下,但发现没有。只有Task.Run可以。因此,x =等待Task.FromResult(0); 等于说x = 0; 令人困惑,但是很高兴知道!
约翰·亨克尔

4
@OlegI:对于I / O操作,最好的解决方案是异步实现它,但有时您没有这种选择。另外,有时您可以同步实现它(例如,缓存的结果,如果不缓存该值,则退回到异步实现)。更一般地,Task返回方法意味着“ 可以是异步的”。因此,有时会为方法提供一个异步签名,这些签名非常清楚一些实现将是同步的(例如,NetworkStream应该是异步的,但MemoryStream应该是同步的)。
斯蒂芬·克莱里

50

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

缓存示例:使用Task.FromResult获取预计算值的缓存示例


并且Task.FromResult可以缓存已完成的任务,例如从中返回的任务。
Paulo Morgado 2013年

1
@Paulo:将整个Task对象保留在内存中似乎比仅缓存结果浪费得多。
Ben Voigt 2014年

2
期望“价值任务”已经被缓存。我不记得到底是哪的人,但我认为Task.FromResult(0)Task.FromResult(1)Task.FromResult(false)Task.FromResult(true)被缓存。您不应该缓存一项网络访问任务,但是从结果中获取一项就可以了。您是否希望每次需要返回值时都创建一个?
Paulo Morgado 2014年

4
...并回答我自己的问题,缓存任务的好处是其中一些任务可以完成,而其他任务可以是尚未完成的任务。呼叫者不必在意:他们进行异步调用,并且如果已经完成,则在等待时会立即得到应答,如果没有,则稍后会得到应答。如果没有这些缓存的任务,要么(a)需要两种不同的机制,一种同步和一种异步-对于调用者来说很麻烦,或者(b)每当调用者要求一个已经可用的答案时,都必须动态创建一个Task(如果我们只缓存了一个TResult)。
ToolmakerSteve

1
我明白了 答案的措词有些混乱。Task本身没有内置的“缓存”机制。但是,如果您编写了一种缓存机制,比如说……下载文件Task <File> GetFileAync(),则可以使用Task.FromResult(cachedFile)立即返回已在缓存中的文件,然后等待可以同步运行,无需线程切换即可节省时间。
Brain2000 '18

31

当您要创建不使用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 Action中使用。ExecuteAsync方法应该是异步的,但是您不必使用async关键字使其异步并可以等待。由于您已经有了结果并且不需要等待任何事情,因此最好使用Task.FromResult。




1

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.