【问题标题】:Using Task.FromResult v/s await in C#在 C# 中使用 Task.FromResult 与 await
【发布时间】:2018-06-06 18:05:40
【问题描述】:

我是 C# 异步编程的新手,需要看看以下哪种方法是处理 Task 对象的首选方法。

我有一个班级可以这样做:

var value = this.SomeFunction(...);

var innerValue = await Task.FromResult(value.Result);

Somefunction 如下所示。

protected async Task<JObject> Somefunction(..)
{
 ..
 returns JBoject
 ..
}

这可以正常工作。现在,我有一个建议,我不应该将Task.FromResultasync 函数一起使用。 相反,我应该使用类似的东西:

var innerValue = await value; //..this works fine too

但我不太确定为什么第一个不是完成相同事情的好习惯。 任何线索都会有所帮助。 谢谢

【问题讨论】:

  • 您对 Task.FromResult(value.Result) 有什么期望?从值任务中获取结果(这很糟糕,因为隐式等待)并用任务包装该值,只是为了等待获得结果

标签: c# asynchronous async-await task


【解决方案1】:

让我们来看看你在做什么,并准确地说出错误的原因。

var value = this.SomeFunction(...);

首先:当类型明确或不重要时使用var类型在这里不清楚,很重要

第二个:使用Async 后缀命名异步函数。

让我们修正您的示例。还是错了,不过现在更清楚了:

Task<Toast> toastTask = this.StartToasterAsync();
Toast toast = await Task.FromResult(toastTask.Result);

这个工作流程完全错误。让我们把它翻译成英文。这是我今天的待办事项清单:

  • 在烤面包机里放一些面包。
  • 在烤面包时,我可以做其他工作,但是,盯着烤面包机看,直到它完成为止
  • 从烤面包机中取出吐司,然后开始新的待办事项列表。
  • 新的待办事项列表是:获得我现在拿着的那块吐司。
  • 执行该待办事项列表。在等待待办事项完成的同时,去做其他工作,但待办事项总是已经完成,因为工作是获得我已经获得的结果。所以不要做其他工作。只需检查一下,是的,我实际上拿着我刚刚写了一份待办事项清单的那块吐司。
  • 现在我有一片吐司。

这个工作流程是一种制作吐司的疯狂方式。它有效——最后你会得到一片吐司——但没有明智的人会这样做,而且你不应该编写等效的计算机程序:

  • 这是一个“异步”工作流程,其中所有可能的异步优势都已被删除
  • 第一步 - 等待烤面包机弹出 - 已同步等待。
  • 第二步——异步等待已经完成的任务——绝不是异步的!

永远不要以这种方式编写异步代码。

制作吐司的正确工作流程是:

  • 将面包放入烤面包机,开始烘烤。
  • 做其他工作直到吐司爆开。
  • 拿来吐司。

正如我们所料,编写程序的正确方法要简单得多:

Task<Toast> toastTask = this.StartToasterAsync(...);
Toast toast = await toastTask;

甚至更好:

Toast toast = await this.StartToasterAsync(...);

您是新手,还没有内化异步工作流上各种操作的含义。你的小程序中的操作是:

  • .Result 表示停止异步并且在结果可用之前什么都不做。请注意,这会导致死锁。您正在停止一个线程,直到结果可用; 如果您要停止将来会产生结果的线程会怎样?例如,假设我们做了一个待办事项列表,“(1)从互联网上订购一盒巧克力。(2)在巧克力到达之前什么都不做。(3)从邮箱中取出巧克力。”该工作流程未完成,因为同步等待结果需要您在未来完成工作
  • await 表示此工作流在结果可用之前无法继续,因此异步等待它。出发去寻找其他工作要做,等结果出来了,我们就从这里重新开始。

请注意,它们的含义相同,因为都是工作流中的点,在结果可用之前工作流不会继续进行。但它们完全不同Result同步 等待,await异步 等待。 确保您理解这一点。这是您必须了解的最基本的一点。

最后

  • FromResult 表示有人需要一个任务,但我已经有了那个任务的结果,所以做一个已经完成的任务。当它被awaited 或Result 调用时,无论哪种方式,它都会立即返回结果。

拨打FromResult 是不寻常的。如果您处于异步工作流程中,通常您只需return result; 表示任务已完成。

【讨论】:

  • 几乎我唯一一次使用FromResult 是当我有一个带有虚拟异步方法的层次结构时,其中一个实现实际上不需要做任何异步工作,所以我更喜欢返回一个已完成的任务,而不是有一个没有awaits 的async-marked 方法。
【解决方案2】:

第一个版本的问题不是Task.FromResult的使用,从文档来看:

创建一个Task&lt;TResult&gt;,它已成功完成并具有指定的结果

问题是调用value.Result 执行同步等待。因此,您实际上是在异步等待同步结果。

请注意,第一个版本是一个错误的包装器(因为必须为 await 调用生成代码):

var value = this.SomeFunction(...);
var innerValue = value.Result;

总结一下,就用这个吧:

var innerValue = await value;

或者,如果valueinnerValue之间没有代码可以运行,你可以完全忽略value的赋值

var innerValue = await this.SomeFunction(...);

【讨论】:

  • 最好的选择是 var innerValue = await this.SomeFunction(...) 并且在大多数情况下是 .ConfigureAwait(False)
  • @SirRufo 我们真的不知道 OP 是否不想在启动 SomeFunction 和需要结果之间运行其他代码,但我将其添加为选项
  • 我们只能说说我们看到的代码。也许 OP 确实想要运行完全不同的代码 - 我们是否也应该谈论这个;o)
猜你喜欢
  • 2015-01-09
  • 2019-01-18
  • 2016-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-23
  • 2013-11-03
相关资源
最近更新 更多