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线程调用它。