gpt4 book ai didi

c# - 使用 SemaphoreSlim 限制对共享变量的访问是否保证所有写入都是可见的?

转载 作者:行者123 更新时间:2023-12-03 12:59:22 25 4
gpt4 key购买 nike

概括

我有一个使用 lock 的类(class)提供对私有(private)字段的线程安全访问。但是,由于下面详述的原因,我正在考虑改用 SemaphoreSlim实现线程安全。我知道如果我用 lock (_lock) 包围对该字段的所有访问权限(正如我目前所做的那样),我保证对该字段的写入将是原子的,并且所有线程都会看到最近写入的值( source )。我想知道我是否可以通过 SemaphoreSlim 获得这两个保证,或者只是关于原子写入的保证。

细节

我有一个我不久前写的接口(interface)来表示可用于与外部设备通信的服务:

public interface ICommunicationService : IDisposable
{
event EventHandler<ReceivedMessageEventArgs> MessageReceived;
void SendMessage(GenericMessage message);
void Start();
}

我最近遇到了一种情况,我希望对这些方法进行异步实现,显然,当您开始执行异步时,您需要全力以赴以避免死锁等,所以我我计划更新接口(interface)和我为支持异步操作而编写的所有现有实现:
public interface ICommunicationService : IDisposable
{
event EventHandler<ReceivedMessageEventArgs> MessageReceived;
Task SendMessage(GenericMessage message);
Task Start();
}

我的一个实现这个接口(interface)的类在它的 SendMessage() 的实现中做了一堆锁定。和 Start() :
public virtual void SendMessage(GenericMessage message)
{
CheckDisposed();

lock (_lock)
{
if (CommunicationState != CommunicationState.Up)
{
throw new InvalidOperationException("Message cannot be sent as communication is not established.");
}

try
{
_currentDelegate.SendMessage(message);
}
catch (Exception)
{
TerminateCommunication();
}
}
}

尝试将此代码切换到异步实现,我注意到我无法等待 lock 中的任务陈述。根据异步大师 Stephen Cleary 的 blog post on the subject ,执行此操作的异步方式是使用 SemaphoreSlim .

根据那篇文章,我更改了我的 SendMessage对此的实现:
public virtual async Task SendMessage(GenericMessage message)
{
CheckDisposed();

if (CommunicationState != CommunicationState.Up)
{
throw new InvalidOperationException("Message cannot be sent as communication is not established.");
}

// _mutex = new SemaphoreSlim(0, 1)
await _mutex.WaitAsync().ConfigureAwait(false);

try
{
await _currentDelegate.SendMessage(message);
}
catch (Exception)
{
TerminateCommunication();
}
finally
{
_mutex.Release();
}
}

我想知道的是,我是否保证任何给定线程都会看到 _currentDelegate 的最新值执行时 await _currentDelegate.SendMessage(message) ,或者我是否需要使用另一个构造来确保写入对其他线程立即可见。

特别是这个类有另一个方法 TryRestartCommunication :
private bool TryRestartCommunication(bool initialStart)
{
// ...
lock (_lock)
{
// ...
try
{
_currentDelegate = _factory();
_currentDelegate.Start();
_currentDelegate.MessageReceived += MessageReceived;
CommunicationState = CommunicationState.Up;
return true;
}
// ...
}
}

如果我重新实现这个方法来锁定信号量并有一些线程 A 调用 TryRestartCommunication()在线程 B 调用 SendMessage() 之后立即,我保证线程 B 会看到 _currentDelegate 的新值吗?由线程 A 设置?

如果没有,一个不错的解决方案是制作 _currentDelegate volatile ,或使用 Interlocked更新它的值(value)?

编辑

获得了近距离投票,因为显然这个问题还不够清楚。让我尽量说清楚:如果我不再使用 lock 保护我的关键区域至 SemaphoreSlim , 那么我是否需要将共享字段标记为 volatile (或类似的东西)以确保相同级别的线程安全?

最佳答案

如果您从通过 lock() 保护您的关键区域/数据切换到 SemaphoreSlim(1),您最终将获得相同级别的线程安全。

我们已经做了好几次,从来没有遇到过单一的数据保护错误。

但是请注意,在 await xxx.ConfigureAwait(false); 之后您可能会以不同的同步上下文(线程)和方法结束await _currentDelegate.SendMessage(message);可能不太好。例如,如果该方法访问 Webform UI 控件。

关于c# - 使用 SemaphoreSlim 限制对共享变量的访问是否保证所有写入都是可见的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46665607/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com