简体   繁体   English

我应该在所有方法中还是仅在第一种方法中使用configure await?

[英]Should I use configure await in all methods or only in the first method?

I have a library with async methods and I have read that for libraries, it is recommended to use ConfigureAwait(false) . 我有一个带有异步方法的库,并且已经阅读了有关库的内容,建议使用ConfigureAwait(false)

For example, if I have something like: 例如,如果我有类似的东西:

public async Task myMethod01()
{
    await myMethod02();
}

private async Task myMethod02()
{
    await myMethod03();
}

private async Task myMethod03()
{
    await anotherMetodAsync().ConfigureAwait(false);
}

The library user can only use method01 , the other 2 methods are private because they are auxliar methods for the main method method01() . 库用户只能使用method01 ,其他两种方法是私有的,因为它们是主要方法method01()辅助方法。

But I don't know if it is needed to use ConfigureAwait only in the first method of the chain call or I should to use in all of them. 但是我不知道是否仅在链调用的第一种方法中需要使用ConfigureAwait ,还是我应该在所有这些方法中使用它。

You should use the ConfigureAwait(false) on all the async calls. 您应该在所有异步调用上使用ConfigureAwait(false) When this is not done, the first async call (without the ConfigureAwait(false) ) will take the SynchronizationContext and this could cause a deadlock in certain conditions (like on ASP.NET) when you wait synchronously on that call. 如果不这样做,则第一个异步调用(不带ConfigureAwait(false) )将使用SynchronizationContext,并且在某些情况下(例如在ASP.NET上)同步等待时,这可能会导致死锁。

My advice is to read this article written by Stephen Cleary. 我的建议是阅读Stephen Cleary撰写的这篇文章 The part you are interested in is: 您感兴趣的部分是:

Using ConfigureAwait(false) to avoid deadlocks is a dangerous practice. 使用ConfigureAwait(false)避免死锁是一种危险的做法。 You would have to use ConfigureAwait(false) for every await in the transitive closure of all methods called by the blocking code, including all third- and second-party code. 在阻塞代码调用的所有方法(包括所有第三方代码ConfigureAwait(false)的传递关闭中,您必须为每次等待使用ConfigureAwait(false) Using ConfigureAwait(false) to avoid deadlock is at best just a hack). 使用ConfigureAwait(false)避免死锁充其量只是一种破解)。

tl;dr tl; dr

Yes , it is needed in order to ensure that all of your asynchronous continuations within your library code are executed on a thread pool thread (depending on the SynchronizationContext / TaskScheduler in use). 是的 ,为了确保您在库代码中的所有异步延续都在线程池线程上执行(取决于所使用的SynchronizationContext / TaskScheduler ),这是必需的。

Would you like to know more? 你想知道更多吗?

Task.ConfigureAwait(Boolean) Task.ConfigureAwait(布尔)

  • true attempts to marshal the remainder of the async method back to the original context captured true尝试将异步方法的其余部分编组回捕获的原始上下文
  • false schedules the remainder of the async method on a thread pool thread false 在线程池线程上调度异步方法的其余部分

Consider the following WPF example: WPF uses the DispatcherSynchronizationContext to resume asynchronous continuations on the UI context, because a background thread cannot update the contents of Controls. 请考虑以下WPF示例:WPF使用DispatcherSynchronizationContext在UI上下文上恢复异步继续,因为后台线程无法更新Control的内容。

private async void Button_Click(object sender, RoutedEventArgs e)
{
    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context

    await CompleteAsynchronously();

    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context

    await CompleteAsynchronously().ConfigureAwait(false);

    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //true
}

private async Task CompleteAsynchronously()
{
    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context

    await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false);

    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //true
}

Here, you see that the continueOnCapturedContext flag of the called method has no effect on the caller. 在这里,您看到被调用方法的continueOnCapturedContext标志对调用者没有影响。 Yet, the called method runs (or at least starts to run) on the thread that the caller was running on, of course. 但是,当然,被调用方法在调用者正在其上运行的线程上运行(或至少开始运行)。

However, the capturing of the current context (either the current SynchronizationContext ; if null then the current TaskScheduler ) only occurs when an incomplete Task is awaited. 但是,仅当等待不完整的Task时才发生对当前上下文(当前SynchronizationContext ;如果为null则当前TaskScheduler )的捕获。 If the Task completes synchronously, continueOnCapturedContext has no effect and the remainder of the method continues to run synchronously on the current thread. 如果Task同步完成,则continueOnCapturedContext无效,该方法的其余部分继续在当前线程上同步运行。

private async void Button_Click(object sender, RoutedEventArgs e)
{
    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context

    await CompleteSynchronously().ConfigureAwait(false);

    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context
}

private async Task CompleteSynchronously()
{
    await Task.Delay(0);
}

So, in your library code (assuming you never require context), you should always use ConfigureAwait(false) in order to ensure that no context is captured for asynchronous continuations, regardless of the framework calling into your assemblies (eg WPF, ASP.NET Core, Console, ...). 因此,在您的库代码中(假设您从不需要上下文),应该始终使用ConfigureAwait(false) ,以确保不捕获用于异步继续的上下文,而不考虑框架调用了程序集(例如WPF,ASP.NET)核心,控制台...)。

For more details, have a look at Best Practices in Asynchronous Programming (ia ConfigureAwait ) in this MSDN Magazine Article by Stephen Cleary . 有关更多详细信息,请参阅Stephen Cleary撰写的《 MSDN杂志》文章 中的异步编程最佳实践 (通过ConfigureAwait )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM