【发布时间】:2014-01-19 15:23:48
【问题描述】:
有没有人遇到过使用ConfigureAwait(true) 的场景?因为true 是默认选项,所以我看不出你什么时候会使用它。
【问题讨论】:
标签: c# asynchronous async-await configureawait
有没有人遇到过使用ConfigureAwait(true) 的场景?因为true 是默认选项,所以我看不出你什么时候会使用它。
【问题讨论】:
标签: c# asynchronous async-await configureawait
true 尝试将延续编组回捕获的原始上下文;否则为假。
实际上更像是说ConfigureAwait(true) 就像使用.ContinueWith( t => {...}, TaskScheduler.FromCurrentSynchronizationContext()),而ConfigureAwait(false) 就像使用.ContinueWith( t => {...})。如果传递 false,则允许继续在线程池线程上运行,而不是拉回当前同步上下文。
【讨论】:
ConfigureAwait(true) 做了什么,但没有解释何时有人会想要这样做。很高兴知道有人想要.ConfigureAwait(true) 的一些例子。是和根本不调用.ConfigureAwait() 一样,还是避免使用.ConfigureAwait() 会使代码随机使用或不使用当前上下文?例如,为什么有人需要在 ASP.NET MVC 应用程序中使用同步上下文?
我可以看到的一种可能性是,如果您正在库中编写代码,并且您希望让您的调用者决定是否适合在原始上下文中继续1(尽管我通常会主张永远不要在库代码中继续使用原始上下文)
您的调用者将传递一个bool 参数或设置一些配置值,因此直到运行时您才会知道正确的参数值是什么。
这是 API 的一般答案类型,例如具有无参数变体和具有单个参数的变体的 API,其中无参数变体被记录为“与具有良好 -已知值 x" - 如果您直到运行时才知道要传递的正确值是什么,那么您应该使用正确的运行时值调用单个参数变体。
1 例如你的来电者也提供了一个代表。您的调用者将知道(并且可以决定)该委托是否需要返回原始上下文。
【讨论】:
.ConfigureAwait(true) 上强制继续执行的一个真实示例。
ConfigureAwait(boolVariable),这很好。但最初的问题是是否有理由这样做ConfigureAwait(true),听起来答案是否定的。
因为 true 是默认选项,我看不出你什么时候会使用它。
一个明显的用例是,当您希望确保每次等待某事时,都会对如何处理同步上下文做出明确而深思熟虑的选择。
来自http://newmedialabs.co.za/blog/post/SynchronizationContexts 的示例政策:
在 NML,我们更喜欢始终明确说明我们希望如何完成任务 继续发生。所以即使一个任务的默认值是 ConfigureAwait(true),我们仍然这样指定它,以便我们 始终了解“幕后”正在发生的事情。
尽管为了提高可读性,他们直接使用扩展名而不是 ConfigureAwait(true):
但是,当您查看大量代码时,有些代码带有 ConfigureAwait(true) 和一些带有 ConfigureAwait(false) 的,它不是 很容易发现它们的不同之处。所以我们使用 ConfigureAwait(false),或者一个有用的扩展方法, ContinueOnCapturedContext()。它做同样的事情,但只是 以更直观的方式将其与 ConfigureAwait(false) 区分开来。
【讨论】:
I’ve recently come to the conclusion that it’s best to always specify ConfigureAwait everywhere lest you forget. 也回应了这一点,它写了一个 Resharper 扩展来解决这个问题。
使用ConfigureAwait(true) 的情况是在锁中执行等待或使用任何其他上下文/线程特定资源时。这需要一个同步上下文,您必须创建它,除非您使用的是自动创建 UI 同步上下文的 Windows 窗体或 WPF。
在以下代码中(假设从 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://docs.microsoft.com/en-us/azure/azure-functions/durable-functions-checkpointing-and-replay#orchestrator-code-constraints 中找到。”。
更多信息可以在here找到。
【讨论】: