我有一个带签名的简单异步方法:
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委托分配给局部变量,第二个将结果直接分配给变量。
这些结果是否相同?两种方法都有任何优势吗?有更好的方法吗?
答案 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