【问题标题】:Using ConfigureAwait(false) for private async methods?将 ConfigureAwait(false) 用于私有异步方法?
【发布时间】:2016-09-15 19:33:09
【问题描述】:

我有 public async 方法,它调用 3 个不同的 API 来获取一些数据,然后将响应发布到下一个 API。现在根据 Stephen cleary 的文章here 来避免死锁:

1.在您的“库”异步方法中,尽可能使用 ConfigureAwait(false)。
2.不要阻塞任务;一直使用异步。

我想知道私有异步方法是否也是如此?当我调用private async 方法时,是否还需要使用ConfigureAwait(false)?所以有点类似

public async Task<int> ProcessAsync(ServiceTaskArgument arg)
{
    // do i need to use ConfigureAwait here while calling private async method?
    var response1 = await GetAPI1().ConfigureAwait(false);

    // do i need to use ConfigureAwait here while calling private async method?
    var response2= await PostAPI2(response1).ConfigureAwait(false);

    // do i need to use ConfigureAwait here while calling private async method?
    await PostAPI3(response2).ConfigureAwait(false);

    return 1;
}

private async Task<string> GetAPI1()
{
    var httpResponse = await _httpClient.GetAsync("api1").ConfigureAwait(false);

    // do i need to use ConfigureAwait here while calling private async method?
    await EnsureHttpResponseIsOk(httpResponse).ConfigureAwait(false);

    return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
}

private async Task<string> PostAPI2(string data)
{
    var stringContent = new StringContent(data, Encoding.UTF8, "application/json");
    var httpResponse = await _httpClient.PostAsync("api2", stringContent).ConfigureAwait(false);

    // do i need to use ConfigureAwait here while calling private async method?
    await EnsureHttpResponseIsOk(httpResponse).ConfigureAwait(false);

    return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
}

private async Task<string> PostAPI3(string data)
{
    var stringContent = new StringContent(data, Encoding.UTF8, "application/json");
    var httpResponse = await _httpClient.PostAsync("api3", stringContent).ConfigureAwait(false);

    // do i need to use ConfigureAwait here while calling private async method?
    await EnsureHttpResponseIsOk(httpResponse).ConfigureAwait(false);

    return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
}

private async Task EnsureHttpResponseIsOk(HttpResponseMessage httpResponse)
{
    if (!httpResponse.IsSuccessStatusCode)
    {
        var content = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
        throw new MyHttpClientException("Unexpected error has occurred while invoking http client.", content, httpResponse.Headers);
    }
}

更新1
我也经历过 SO 帖子 here,但回答建议使用自定义 NoSynchronizationContextScope
我想知道是否需要在私有方法上使用 ConfigureAwait(false)?

【问题讨论】:

    标签: c# async-await


    【解决方案1】:

    我想知道是否需要在私有方法上使用 ConfigureAwait(false)?

    一般来说,是的。 ConfigureAwait(false) 应该用于每个 await,除非方法需要它的上下文。

    但是,如果您使用NoSynchronizationContextScope,则无需使用ConfigureAwait(false)

    【讨论】:

    • @Stephen Cleary 我们还必须在asp.net core 中使用ConfigureAwait(false) 还是只在经典asp.net 中出现死锁问题
    • @LP13:死锁只发生在经典的 ASP.NET 上。 On ASP.NET Core ConfigureAwait(false) has no effect然而,ASP.NET Core修复了所有需要阻塞的地方。所以阻塞异步代码仍然不是推荐
    • 我不认为 NoSynchronizationContextScope 与异步方法一起使用是安全的。如果在范围内使用await,并且await 的延续在随机线程池线程而不是原始线程上运行,Dispose 将导致SynchronizationContext.SetSynchronizationContext(_synchronizationContext) 覆盖该随机线程的当前同步上下文!在 UI 上下文中,之后您需要一个 await Thread.Yield() 来实际触发返回 UI 线程的延续。
    • @Ryan: NoSynchronizationContextScope 可以与async 方法一起使用只要它用于阻止异步方法。在 using 块中使用 await 而不是 Wait 会导致您描述的问题。出于这个原因,我写了一个 NoContext method 来强制正确使用。
    【解决方案2】:

    这取决于你的班级是什么。它是类库的一部分吗?或者是web api还是其他服务操作?正如斯蒂芬所提到的,ConfigureAwait(false) 避免死锁是最安全的,但如果您绝对确定调用者没有阻塞(.Wait().Result() ) 例如,如果 ProcessAsync 是一个 asp.net web api 方法。

    【讨论】:

    • 如果您确实确定代码不需要上下文,那么添加 ConfigureAwait 可以防止将每个延续调度到同步上下文中。即使它没有死锁,它也会影响性能,并且还可能延迟实际需要该同步上下文的操作的处理。特别是对于可能被大量使用的库方法,其中至少有一些对性能敏感,这是值得做的。
    • @Servy 不错的补充。我被卖了:)
    • @Servy 谢谢。正如建议的那样,我将添加 ConfigureAwaite(false) 。但我真的很想知道为什么这不是 .net 中的默认实现。?
    • @LP13 因为几乎所有开发库的人都想捕捉上下文,而且编写业务线程序的人比编写库的人多得多。这样,编写库的人通常会比 LOB 开发人员更了解如何处理此类事情。
    猜你喜欢
    • 2015-01-22
    • 2017-06-21
    • 2018-09-10
    • 1970-01-01
    • 2021-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多