假设我有一个生成int
的{{3}},以及接受 int
的回调:
Task<int> task = ...;
Action<int> f = ...;
现在我想设置它,以便一旦任务完成并返回其整数结果,将使用该整数调用callfack f
- 无需主线程等待任务完成:
据我了解,典型的解决方案是Task
方法:
task.ContinueWith(t => f(t.Result));
但是,也可以为任务获得Task.ContinueWith
,并使用其类似事件的界面:
TaskAwaiter<int> awaiter = task.GetAwaiter();
awaiter.OnCompleted(() => f(awaiter.GetResult()));
现在,我意识到TaskAwaiter
不适合在应用程序代码中常用,主要存在于await
关键字内部使用。
但为了加深对TPL的理解,我想知道:
解决方案(1)和(2)之间是否存在实际差异?
例如,
答案 0 :(得分:1)
一个区别是
task.ContinueWith(t => f(t.Result));
不会捕获当前的同步上下文,例如,在UI应用程序中 - 将在线程池线程上执行回调。而
TaskAwaiter<int> awaiter = task.GetAwaiter();
awaiter.OnCompleted(() => f(awaiter.GetResult()));
将捕获同步上下文,并且将在UI线程上执行回调。
当然,您可以使用ContinueWith
:
task.ContinueWith(r => f(r.Result), TaskScheduler.FromCurrentSynchronizationContext());
但那并不是你所使用的问题。因此,至少在这方面,问题中提供的方式是不同的。
异常表示也有区别,如果任务出现故障,则访问Task.Result
会抛出AggregateException
(一个或多个异常作为内部异常),而访问awaiter.GetResult()
则不会在AggregateException
中抛出异常并将按原样重新抛出(如果有多个异常,例如来自Task.WhenAll
- 除了一个之外的所有异常将被忽略,只会抛出一个异常。)