使用异步方法的结果

时间:2015-06-08 09:44:14

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

我有一个带签名的简单异步方法:

bool response = null;
// option 1
await this.transportService.AllowAccessAsync(authenticatedUserToken).ContinueWith(task => response = task.Result);
// option 2
response = await this.transportService.AllowAccessAsync(authenticatedUserToken);

调用此方法时,在将结果分配给局部变量时,我似乎有两个选项:

SELECT p.*
     , s.stock
  FROM products p
  JOIN stock s
    ON s.id = p.id
+------+-------+------+-------+
| id   | parid | name | stock |
+------+-------+------+-------+
|    1 |     1 | A    |   113 |
|    2 |     1 | B    |   113 |
|    3 |     2 | C    |     0 |
|    4 |     3 | D    |    50 |
+------+-------+------+-------+

第一个使用continuation委托分配给局部变量,第二个将结果直接分配给变量。

这些结果是否相同?两种方法都有任何优势吗?有更好的方法吗?

4 个答案:

答案 0 :(得分:6)

  

这些结果是否相同?

修改

@Servy正确地指出,因为ContinueWith只是结果的投影。这意味着两个操作在语义上都是等效的,但是例外的是它们的行为会有所不同。

  

这两种方法都有任何优势吗?有更好的方法吗?

编辑2:

以下注释通常与async-await vs ContinueWith的使用有关。明确地看两个例子,因为它们都使用async-await,肯定使用后者,因为它们都包含状态机生成,但后者将传播AggregateException以防发生异常。

async-await产生状态机的开销最小,而ContinueWith则不然。另一方面,使用async-await可让您在实际异步时“感觉”同步,并为您节省ContinueWith的详细程度。我肯定会选择async-await,但我建议你研究使用它的正确途径,因为可能会出现意外的可怜。

答案 1 :(得分:4)

通过async/await使用任务并行库ContinueWith,您可以不必要地混合模式。除非你别无选择,否则有很多理由不这样做,尤其是async/await SynchronizationContext is not preserved by the default implementation of ContinueWith

简而言之,选项2 是正确的。

答案 2 :(得分:4)

  

这些结果是否相同?

是的,这两个选项最终会将相同的结果设置为response 。当出现异常时,唯一的区别就在于此。在第一个选项中抛出的异常将是实际异常的AggregateException包装,而在第二个选项中它将是实际的异常。

  

这两种方法都有任何优势吗?

以这种方式使用ContinueWith绝对没有优势。第二种选择具有更好的异常处理和更易于编写和读取的优点。

  

有更好的方法吗?

不是,这就是你使用async-await的方式。

正如Servy评论的那样,这并不是ContinueWith的意思。 ContinueWith等价物是将方法的其余部分放入延续中。所以不要这样:

public async Task FooAsync()
{
    var response = await this.transportService.AllowAccessAsync(authenticatedUserToken);
    Console.WriteLine(response);
}

你会这样做:

public Task FooAsync()
{
    return this.transportService.AllowAccessAsync(authenticatedUserToken).
        ContinueWith(task => Console.WriteLine(task.GetAwaiter().GetResult()));
}

这确实有一些性能优势,因为它不需要async-await状态机,但是要做到这一点非常复杂。

答案 3 :(得分:2)

它更多地与编码风格有关。

当您有一个大型工作流可能希望在执行promise时分配不同的continuation时,第一个样式可能很有用。但它依赖于闭包。我不推荐这种用法,因为它并不总是可预测的。创建工作流时应使用ContinueWith,并且每个步骤仅依赖于前一个步骤,并且不与外部作用域通信,除非通过提供产生最终结果的任务(您将等待)。

当你对结果感兴趣时,第二个是有用的。

同样ContinueWith允许您指定TaskScheduler,因为您的应用程序默认使用的可能不是您想要的那个。

有关TaskScheduler的更多信息,请点击此处: http://blog.stephencleary.com/2015/01/a-tour-of-task-part-7-continuations.html