使用ContinueWith时ConfigureAwait(false)

时间:2017-05-23 08:41:17

标签: c#

等同于使用:

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的重载。否则,它默认运行而不考虑执行上下文。

但是,如果我错了,我仍然会要求确认或更正。

2 个答案:

答案 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传递给ContinueWithStartNew

如果您想要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.SetContinuationForAwaitcontinueOnCapturedContextfalse }),它落入:

if (!AddTaskContinuation(tc, addBeforeOthers: false))
        tc.Run(this, bCanInlineContinuationTask: false);

基本上是ContinueWith。所以:是的。