有人遇到过使用
ConfigureAwait(true)
的场景吗?由于 true
是默认选项,我不知道你什么时候会使用它。
尝试将延续编组回到捕获的原始上下文;否则,错误。
实际上更像是说
ConfigureAwait(true)
就像使用 .ContinueWith( t => {...}, TaskScheduler.FromCurrentSynchronizationContext())
,其中 ConfigureAwait(false)
就像使用 .ContinueWith( t => {...})
。如果传递 false,则允许延续在线程池线程上运行,而不是拉回到当前同步上下文。
我可以看到的一种可能性是,如果您在库中编写代码,并且您希望允许调用者决定是否适合继续使用原始上下文1(尽管我通常认为永远不要继续使用原始上下文)库代码中的上下文)
您的调用者将传递
bool
参数或设置某些配置值,因此直到运行时您才会知道正确的参数值是什么。
这是 API 的一般答案类型,例如具有无参数变体和具有单个参数的变体,其中无参数变体被记录为“与具有已知值的单个参数变体相同” x” - 如果您直到运行时才知道要传递的正确值是什么,那么您应该只使用正确的运行时值调用单参数变体。
1您的呼叫者还提供了一个代表。您的调用者将知道(并可以决定)该代表是否需要返回原始上下文。
由于 true 是默认选项,我不知道你什么时候会使用它。
一个明显的用例是,当您想要确保每次等待某件事时,都会对如何处理同步上下文做出明确且深思熟虑的选择。
来自 http://newmedialabs.co.za/blog/post/SynchronizationContexts的示例策略:
在 NML,我们更喜欢始终明确说明我们想要如何完成任务 持续发生。所以即使任务的默认值是 ConfigureAwait(true),我们仍然这样指定它,以便我们 始终了解“幕后”正在发生的事情。
尽管为了提高可读性,他们使用扩展名而不是直接使用
ConfigureAwait(true)
:
但是,当你查看大量代码时,有些代码带有 有的用ConfigureAwait(true),有的用ConfigureAwait(false),不是 很容易发现它们的不同之处。所以我们使用 ConfigureAwait(false),或者一个有用的扩展方法, 继续捕获上下文()。它做同样的事情,但只是 以更直观的方式将其与ConfigureAwait(false)区分开来。
使用
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();
}
}
如果您使用 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。”。
更多信息可以在这里找到。
从行为角度来看,
.ConfigureAwait(true)
与完全省略ConfigureAwait
完全一样。两者之间绝对没有行为差异。因此,使用 .ConfigureAwait(true)
的唯一可能原因是为了表达意图。换句话说,您可以使用 .ConfigureAwait(true)
对程序员同事说些什么,而不是对编译器说些什么。
就我个人而言,我已经犯过足够多的错误,将
.ConfigureAwait(true)
误读为 .ConfigureAwait(false)
,从而得出结论:永远不应该使用 .ConfigureAwait(true)
。相反,我更喜欢通过添加以下代码注释来明确我的意图:
// Continue on captured context
可以在这个答案中找到示例。