C# 您是否可以在没有线程安全的情况下使用ConfigureWait(false)?
我看到各地的人都建议在可以使用的地方使用C# 您是否可以在没有线程安全的情况下使用ConfigureWait(false)?,c#,multithreading,asynchronous,.net-core,thread-safety,C#,Multithreading,Asynchronous,.net Core,Thread Safety,我看到各地的人都建议在可以使用的地方使用ConfigureAwait(false),这是图书馆作者的必备品,等等 但是既然ConfigureAwait(false)的延续可以在线程池中的任何线程上运行,那么如何安全地防止多个线程访问库中的相同状态 假设您的库具有以下API: async Task FooAsync() { // Do something //barAsync and saveToFileAsync are private methods. awa
ConfigureAwait(false)
,这是图书馆作者的必备品,等等
但是既然ConfigureAwait(false)
的延续可以在线程池中的任何线程上运行,那么如何安全地防止多个线程访问库中的相同状态
假设您的库具有以下API:
async Task FooAsync()
{
// Do something
//barAsync and saveToFileAsync are private methods.
await barAsync().ConfigureAwait(false);
// counter is a private field
counter++;
await saveToFileAsync().ConfigureAwait(false);
// Do other things
}
如果UI线程一直调用此fooancy
(例如,由于用户按下按钮),此代码是否会损坏计数器的值和保存的文件?因为可能正在执行多个线程
我发现使用ConfigureAwait(false)
而不使用线程安全是很困难的,除了不修改状态的简单情况
使现代化
我可能不太清楚,但在我们的团队中,我们决定采用单线程。因此,从下面的答案来看,我们似乎不能使用ConfigureAwait(false)
,因为它引入了并行的可能性,需要使用锁等来控制。ConfigureAwait
与线程安全无关。这是关于避免捕捉上下文
如果希望代码是线程安全的,那么应该实现它。这通常涉及使用某种同步构造,例如锁
如前所述,即使删除对ConfigureAwait(false)
的调用,您的fooancy()
也不是线程安全的。两个或多个线程仍然可以同时调用它,即使在有SynchronizationContext
可用的UI应用程序中也是如此
如何安全地防止多个线程访问库中的同一状态
通过同步对任何共享资源的访问。假设计数器
是代码中唯一的关键部分,则可以使用联锁。增量
API:
async Task FooAsync()
{
...
Interlocked.Increment(ref counter);
...
}
这将增加计数器
,并将新结果存储为原子操作
还有许多其他的同步构造。使用哪一个取决于你基本上在做什么。避免调用ConfigureAwait(false)
并不是使代码线程安全的方法
但是,既然ConfigureAwait(false)的延续可以在线程池中的任何线程上运行,那么如何安全地防止多个线程访问库中的相同状态
await
确实引入了重入的可能性,但让它实际导致问题的情况很少。异步代码本质上鼓励一种功能性更强的结构(方法的输入是它的参数,输出是它的返回值)。异步方法可能会产生副作用并依赖于状态,但这并不常见
请注意,wait
导致意外重入ConfigureAwait(false)
将在线程池上恢复,但这不会导致此处出现问题
如果UI线程一直调用此FooAsync(例如,由于用户按下按钮),此代码是否会损坏计数器的值和保存的文件?因为可能正在执行多个线程
是的,差不多。是的,计数器可能会获得意外值,但这不一定是因为多个线程。考虑相同的代码,没有<代码> ConfigureAwait(false)< /> >:您仍然有多个函数调用,只在单个线程上运行。他们仍在为反垄断和任何其他共同的国家而斗争。在这种情况下,由于单线程,计数器+++
是原子的,但由于它是共享的,因此该函数的单个调用可能会在从等待
恢复时看到值意外更改
使用
ConfigureAwait(false)
,您确实会额外担心意外并行(使用await
您会意外重新进入),因此如果您使用非线程安全共享状态,情况可能会变得更糟。可重入性可能导致意外状态,但并行性可能导致无效状态。您不能。不知道还能说什么。对于该上下文,即使没有使用ConfigureAwait,您的代码也不是线程安全的,因为您无法保证调用方首先在UI线程上。您可能需要研究在该点锁定建议使用ConfigureAwait(false),只要代码不了解上下文。一个例子是asp.NET应用程序中的HttpContext。您不能从请求线程以外的其他线程访问它。@lase您是对的。大多数人的设计不是为了线程安全,他们认为只有一个线程在运行,这是默认的。普通的async/await与此非常契合,但是,在添加ConfigureAwait(false)的那一刻,您就需要线程安全,但在我访问的大多数问题中,似乎没有人能够解决这个问题,这给人的印象是ConfigureAwait(false)是一个简单的更改,但不是,它有后果,可能导致损坏状态。大多数人的设计不是为了线程安全
我不认为这是真的。大多数好的库都是线程安全的,因为存在的小全局状态被锁定。除非特别说明(例如Concurrent…
classes),否则不期望单个对象是线程安全的。例如,List
作为单个对象不是线程安全的SqlConnection
不是线程安全的,但使用的是内部连接池。谢谢,@mm8。我想我可能不太清楚。如果在我们的团队中,我们决定使用单线程,那么ConfigureAwait(false)
似乎打破了这个角色。至于fooancy()
,保证将从UI线程调用它。