库中的ConfigureAwait(false)是否会丢失调用应用程序的同步上下文?

时间:2015-09-12 19:31:22

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

我已经从比我聪明的人那里多次阅读了这个建议,并且几乎没有任何警告:始终在库代码中使用ConfigureAwait(false) 。所以我相当肯定我知道答案,但我想成为100%。方案是我有一个库,它包含了一些其他异步库。

图书馆代码:

public async Task DoThingAsyc() {
    // do some setup
    return await otherLib.DoThingAsync().ConfigureAwait(false);
}

申请代码:

// need to preserve my synchronization context
await myLib.DoThingAync();
// do I have my context here or did my lib lose it?

3 个答案:

答案 0 :(得分:7)

没有

SynchronizationContext上捕获awaitConfigureAwait配置特定的await

如果应用程序调用库的异步方法并等待它,则无论呼叫内部发生什么,都会在现场捕获SC。

现在,因为异步方法的同步部分(在第一个await之前的部分)在等待返回任务之前执行,所以你可以在那里弄乱SynchronizationContext,但是ConfigureAwait不这样做。

在您的具体示例中,您似乎从异步方法返回ConfigureAwait的结果。这不可能发生,因为ConfigureAwait返回ConfiguredTaskAwaitable结构。但是,如果我们更改方法返回类型:

public ConfiguredTaskAwaitable DoThingAsyc() 
{
    return otherLib.DoThingAsync().ConfigureAwait(false);
}

然后等待它确实会影响调用代码的等待行为。

答案 1 :(得分:2)

来自http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx

的示例
  

......从逻辑上讲,你可以想到以下代码:

await FooAsync();
RestOfMethod();
     

本质上与此相似:

var t = FooAsync();
var currentContext = SynchronizationContext.Current;
t.ContinueWith(delegate
{
    if (currentContext == null)
        RestOfMethod();
    else
        currentContext.Post(delegate { RestOfMethod(); }, null);
}, TaskScheduler.Current);

这意味着您应该在await myLib.DoThingAync();来电之后回复上下文。

答案 2 :(得分:2)

如果在异步调用的逻辑链中使用不一致,ConfigureAwait(false)可能会添加冗余上下文切换(通常表示冗余线程切换)。当存在同步上下文时,这可能发生在逻辑堆栈上的某些异步调用使用ConfigureAwait(false)而某些异常调用使用ConfigureAwait(false)时(更多here)。

您仍然应该在代码中使用public async Task DoThingAsyc() { // do some setup await Task.Run(() => otherLib.DoThingAsync()).ConfigureAwait(false); // do some other stuff } ,但是您可能想要查看您正在调用的第三方代码并减轻与此类内容的任何不一致:

async/await

这将添加一个额外的线程切换,但可能会阻止许多其他。

此外,如果您正在创建一个如您所示的非常薄的包装器,您可能希望像下面那样实现它,而不需要public Task DoThingAsyc() { // do some setup return otherLib.DoThingAsync(); }

if ($scope.tokenValidate.valid=='true') {
    $scope.header = "Choose a new password";
} else if ($scope.tokenValidate.expired=='true') {
    $scope.header = "This link has expired.";
    $scope.mesage = "Enter your email address to receive a new link."
} else {
    $scope.header = "This link is invalid."
}