什么时候应使用ConfigureAwait(true)?

56

有人遇到过使用ConfigureAwait(true)的场景吗?由于true是默认选项,我无法看出何时会使用它。

有人碰到过需要使用ConfigureAwait(true)的情况吗?因为true是默认选项,所以我看不出什么情况下会用到它。
5个回答

25

如果设置为 true,则尝试将续订返回到原始捕获的上下文;否则为 false。

实际上,可以将 ConfigureAwait(true) 看作是使用 .ContinueWith( t => {...}, TaskScheduler.FromCurrentSynchronizationContext()),而 ConfigureAwait(false) 则相当于使用 .ContinueWith( t => {...})。如果传递 false,则续订将在线程池线程上运行,而不是返回到当前同步上下文。


6
很好的解释,尽管这只解释了ConfigureAwait(true)的作用,但并没有解释何时有人想要使用它。能否提供一些例子,使人们知道何时需要.ConfigureAwait(true)。它是否相当于根本不调用.ConfigureAwait(),或避免使用.ConfigureAwait()将使代码随机使用或不使用当前上下文?例如,为什么有人需要在ASP.NET MVC应用程序中使用同步上下文? - Alisson Reinaldo Silva
1
要了解更多关于SynchronizationContext的信息,请参见什么是SynchronizationContext - Majid

12

我能看到的一种可能性是,如果你正在一个库中编写代码,而你希望允许调用者决定是否在原始上下文1上继续(尽管我通常会建议从库代码内部永远不要在原始上下文上继续执行)。

你的调用者将传递一个布尔参数或设置某些配置值,因此在运行时你将不知道正确的参数值是什么。


对于这种具有无参数变体和单参数变体的API的一般类型答案是,如果无法在运行时确定正确的值,则应使用具有正确运行时值的单参数变体直接调用它。无参数变体被记录为“与具有众所周知的值x的单参数变体相同”。


1例如,你的调用者也提供了一个委托。你的调用者将知道(并且可以决定)该委托是否需要回到原始上下文中。


2
这个答案完美地描述了Polly后来采取的方法。如果调用者正在使用自定义TaskScheduler,那么强制继续在原始上下文中进行.ConfigureAwait(true)是一个现实世界的例子。 - mountain traveller
一个例外 - 线程静态访问,如DbCommandInterceptor模式 - jjxtra
4
е»әи®®и°ғз”ЁConfigureAwait(boolVariable)пјҢиҝҷжҳҜеҫҲеҘҪзҡ„гҖӮдҪҶеҺҹй—®йўҳжҳҜжҳҜеҗҰжңүзҗҶз”ұжү§иЎҢConfigureAwait(true)пјҢеӣһзӯ”дјјд№ҺжҳҜеҗҰе®ҡзҡ„гҖӮ - BVernon
尽管我通常会主张从库代码内部永远不要继续原始上下文,但为什么? - John
MudBlazor有一个Task ShowMessageBox(...)方法,它会等待用户关闭对话框。如果我不得不在所有这些方法上添加ConfigureAwait修饰符,那将非常奇怪。同样,包装所有这样的方法也是不合理的。 - John
显示剩余3条评论

6
由于true是默认选项,我无法看出什么时候会使用它。
一个明显的用例是当您想确保每次等待某些内容时,都要对同步上下文做出明确和有意的选择。
来自http://newmedialabs.co.za/blog/post/SynchronizationContexts的示例策略:
在NML中,我们更喜欢明确说明我们希望如何发生任务继续。因此,即使任务的默认值为ConfigureAwait(true),我们仍将其指定为这样,以便我们始终知道“底层”发生了什么。
尽管为了提高可读性,他们使用扩展而不是直接使用ConfigureAwait(true)
然而,当你查看大量的代码时,有些使用ConfigureAwait(true),有些使用ConfigureAwait(false),很难发现它们之间的区别。因此,我们使用ConfigureAwait(false)或一个有用的扩展方法ContinueOnCapturedContext()。它执行相同的操作,但以更直观的方式区分于ConfigureAwait(false)。

2
这也得到了Eli Arbel的支持:“我最近得出结论,最好在任何地方都指定ConfigureAwait,以免忘记。”他编写了一个Resharper扩展来解决这个问题。 - hlovdal
1
链接newmedialabs.co.za已经无法访问。但是我从那篇博客中保存了(在GitHub上)关于同步上下文、任务继续以及方便的扩展方法的基本信息 - AlexMelw
1
这是唯一回答“为什么使用它,既然 true 是默认值”的答案。 - fotisgpap
2
我认为不应该有默认值,调用者应该指定。很多人并没有意识到这个存在以及它的影响。 - rollsch

5
使用ConfigureAwait(true)的情况是在锁定中执行await,或使用其他上下文/线程特定资源时。这需要一个同步上下文,除非您正在使用Windows Forms或WPF,否则您将需要创建它们自己的UI同步上下文。
在以下代码中(假设从UI线程和同步上下文调用),如果使用了ConfigureAwait(false),则锁定会尝试在不同的线程上释放,导致异常。这是一个简单的示例,如果外部源更改了大型配置文件,则尝试更新它,以避免磁盘IO的写操作,如果配置文件与之前相同。
示例:
/// <summary>
/// Write a new config file
/// </summary>
/// <param name="xml">Xml of the new config file</param>
/// <returns>Task</returns>
public async Task UpdateConfig(string xml)
{
    // Ensure valid xml before writing the file
    XmlDocument doc = new XmlDocument();
    using (XmlReader xmlReader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { CheckCharacters = false }))
    {
        doc.Load(xmlReader);
    }
    // ReaderWriterLock
    configLock.AcquireReaderLock(Timeout.Infinite);
    try
    {
        string text = await File.ReadAllTextAsync(ConfigFilePath).ConfigureAwait(true);

        // if the file changed, update it
        if (text != xml)
        {
            LockCookie cookie = configLock.UpgradeToWriterLock(Timeout.Infinite);
            try
            {
                // compare again in case text was updated before write lock was acquired
                if (text != xml)
                {
                    await File.WriteAllTextAsync(ConfigFilePath, xml).ConfigureAwait(true);
                }
            }
            finally
            {
                configLock.DowngradeFromWriterLock(ref cookie);
            }
        }
    }
    finally
    {
        configLock.ReleaseReaderLock();
    }
}

4

如果您正在使用Azure的Durable Functions,则在等待Activity函数时必须使用ConfigureAwait(true)

string capture = await context.CallActivityAsync<string>("GetCapture", captureId).ConfigureAwait(true);

否则,您很可能会遇到以下错误:
“检测到多线程执行。如果编排函数代码等待未由 DurableOrchestrationContext 方法创建的任务,则可能会发生此情况。更多细节请参阅本文:https://learn.microsoft.com/en-us/azure/azure-functions/durable-functions-checkpointing-and-replay#orchestrator-code-constraints。”。
更多信息可以在这里找到。

2
我认为他问的不是你希望值为真的时间,而是在已经是默认值的情况下,你何时想要指定它。 - BVernon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接