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

问题描述 投票:0回答:6

有人遇到过使用

ConfigureAwait(true)
的场景吗?由于
true
是默认选项,我不知道你什么时候会使用它。

c# asynchronous async-await configureawait
6个回答
23
投票

尝试将延续编组回到捕获的原始上下文;否则,错误。

实际上更像是说

ConfigureAwait(true)
就像使用
.ContinueWith( t => {...}, TaskScheduler.FromCurrentSynchronizationContext())
,其中
ConfigureAwait(false)
就像使用
.ContinueWith( t => {...})
。如果传递 false,则允许延续在线程池线程上运行,而不是拉回到当前同步上下文。


12
投票

我可以看到的一种可能性是,如果您在库中编写代码,并且您希望允许调用者决定是否适合继续使用原始上下文1(尽管我通常认为永远不要继续使用原始上下文)库代码中的上下文)

您的调用者将传递

bool
参数或设置某些配置值,因此直到运行时您才会知道正确的参数值是什么。


这是 API 的一般答案类型,例如具有无参数变体和具有单个参数的变体,其中无参数变体被记录为“与具有已知值的单个参数变体相同” x” - 如果您直到运行时才知道要传递的正确值是什么,那么您应该只使用正确的运行时值调用单参数变体。


1您的呼叫者还提供了一个代表。您的调用者将知道(并可以决定)该代表是否需要返回原始上下文。


6
投票

由于 true 是默认选项,我不知道你什么时候会使用它。

一个明显的用例是,当您想要确保每次等待某件事时,都会对如何处理同步上下文做出明确且深思熟虑的选择。

来自 http://newmedialabs.co.za/blog/post/SynchronizationContexts的示例策略:

在 NML,我们更喜欢始终明确说明我们想要如何完成任务 持续发生。所以即使任务的默认值是 ConfigureAwait(true),我们仍然这样指定它,以便我们 始终了解“幕后”正在发生的事情。

尽管为了提高可读性,他们使用扩展名而不是直接使用

ConfigureAwait(true)

但是,当你查看大量代码时,有些代码带有 有的用ConfigureAwait(true),有的用ConfigureAwait(false),不是 很容易发现它们的不同之处。所以我们使用 ConfigureAwait(false),或者一个有用的扩展方法, 继续捕获上下文()。它做同样的事情,但只是 以更直观的方式将其与ConfigureAwait(false)区分开来。


4
投票

使用

ConfigureAwait(true)
的一种情况是在锁中执行等待,或使用任何其他上下文/线程特定资源。这需要一个同步上下文,您必须创建该同步上下文,除非您使用的是 Windows 窗体或 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();
    }
}

3
投票

如果您使用 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。”。

更多信息可以在这里找到。


0
投票

从行为角度来看,

.ConfigureAwait(true)
与完全省略
ConfigureAwait
完全一样。两者之间绝对没有行为差异。因此,使用
.ConfigureAwait(true)
的唯一可能原因是为了表达意图。换句话说,您可以使用
.ConfigureAwait(true)
对程序员同事说些什么,而不是对编译器说些什么。

就我个人而言,我已经犯过足够多的错误,将

.ConfigureAwait(true)
误读为
.ConfigureAwait(false)
,从而得出结论:永远不应该使用
.ConfigureAwait(true)
。相反,我更喜欢通过添加以下代码注释来明确我的意图:

// Continue on captured context

可以在这个答案中找到示例。

© www.soinside.com 2019 - 2024. All rights reserved.