等同于使用:
await task.ConfigureAwait(false);
使用这样的延续(不使用C#编译器' s async/await
转换)?
var taskOfString = ScheduleWorkOnThreadPoolAsync();
// I'd like this continuation to not have to
// "flow" the synchronization context but to simply
// execute wherever it can, i.e. I'd like to tell is
// ConfigureAwait(false) for its previous task.
// How do I do that?
taskOfString.ContinueWith(t => { });
public async Task<string> ScheduleWorkOnThreadPoolAsync()
{
return Task.Run(() => return "Foo" );
}
我假设什么都不做,即保持原样等同于调用ConfigureAwait(false)
,这也是我在调试代码时看到的情况。它可以在任何线程上跳跃。
只有当我们要指定调度程序或同步上下文来运行延续时,才需要将额外信息传递给接受TaskScheduler
的重载。否则,它默认运行而不考虑执行上下文。
但是,如果我错了,我仍然会要求确认或更正。
答案 0 :(得分:8)
使用这样的continuation(不使用C#编译器的async / await转换)?
您几乎应该始终使用async
/ await
。它们具有更安全的默认行为。 ContinueWith
是一种危险的低级API。
我假设什么也不做,即只是将它保留为原来等同于调用ConfigureAwait(false)...只有当我们想要指定调度程序或同步上下文来运行我们需要传递的延续时在接受TaskScheduler的重载的额外信息中。否则,它默认运行而不考虑执行上下文。
没有。这是不正确的,虽然简单的测试不会揭示问题。
正如我在why you shouldn't use ContinueWith
的博文中所描述的那样,TaskScheduler
的默认ContinueWith
是不是 TaskScheduler.Default
。它是TaskScheduler.Current
。由于这在每种情况下都会引起混淆,您应始终将TaskScheduler
传递给ContinueWith
和StartNew
。
如果您想要await x.ConfigureAwait(false)
行为,您实际上会这样做:
var continuation = x.ContinueWith(callback, CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach,
TaskScheduler.Default);
TaskContinuationOptions.ExecuteSynchronously
模仿synchronous-if-possible behavior of await
。如果延续任务是attached to,则TaskContinuationOptions.DenyChildAttach
可以防止出现问题(当子任务通过AttachedToParent
附加到子任务时,用于异步的任务会出现令人惊讶的行为)。 TaskScheduler.Default
模拟始终在线程池上下文中执行的ConfigureAwait(false)
行为。
作为最后一点,你可能需要对continuation
做一些事情 - 至少要观察异常并以某种方式处理它们。
此时,应该清楚为什么我推荐await
。在最坏的情况下,你只需要添加一个辅助方法来使用await
而不是ContinueWith
- await
更易于维护,IMO。
答案 1 :(得分:1)
我已经通过ConfiguredTaskAwaitable
查看了Task
然后GetAwaiter.OnCompleted
的参考来源,回到Task.SetContinuationForAwait
(continueOnCapturedContext
为false
}),它落入:
if (!AddTaskContinuation(tc, addBeforeOthers: false))
tc.Run(this, bCanInlineContinuationTask: false);
基本上是ContinueWith
。所以:是的。