[英]Solving race condition using SemaphoreSlim
当两个线程同时访问共享变量时,就会出现竞争条件。 第一个线程读取变量,第二个线程从变量中读取相同的值。
我在StartAsync
中使用了SemaphoreSlim
来防止竞争条件,即两个线程可能同时更改_clientWebSocket
。 如果我们在Timer
中调用 StartAsync 可能会发生这种情况。
StopAsync
怎么样? 如果 StopAsync 没有包装在 SemaphoreSlim 中,那么两个线程通过if
条件并调用 CloseOutputAsync 两次的机会是多少? 第一个将成功关闭 output,但第二个调用将失败并出现 WebSocketException,因为 web 套接字已关闭。 考虑到我们没有改变任何变量,这是否也是一种竞争条件? 考虑到我的代码的其他部分没有关闭 web 套接字,它是否会发生,我真的需要包装 CloseOutputAsync 的 try/catch 吗?
我在 StopAsync 中重用了 SemaphoreSlim 以防止同时调用 StartAsync 和 StopAsync,这是个好主意吗?
private readonly SemaphoreSlim _semaphore = new(1, 1);
public async Task StartAsync(string url)
{
await _semaphore.WaitAsync().ConfigureAwait(false);
try
{
var ws = new ClientWebSocket();
_clientWebSocket = ws;
await _clientWebSocket.ConnectAsync(new Uri(url), CancellationToken.None).ConfigureAwait(false); // TODO: Handle
_tokenSource = new CancellationTokenSource();
_ = ReceiveLoopAsync(ws, _tokenSource.Token);
_ = SendLoopAsync(ws);
}
finally
{
_semaphore.Release();
}
}
public async Task StopAsync()
{
await _semaphore.WaitAsync().ConfigureAwait(false);
try
{
if (_clientWebSocket is { State: not (WebSocketState.Aborted or WebSocketState.Closed or WebSocketState.CloseSent) })
{
try
{
await _clientWebSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception) // thrown when we try to close an already closed socket
{
}
}
_clientWebSocket?.Dispose();
_clientWebSocket = null;
_tokenSource?.Cancel();
}
finally
{
_semaphore.Release();
}
}
我会说,如果您希望StopAsync
被并行调用,那么这些措施是值得的。
尽管我在您的代码中看到了另一个潜在缺陷 - 您在开始和停止之间共享_clientWebSocket
和_tokenSource
,因此您可能会在下一种情况下结束:
StartAsync
并初始化_clientWebSocket
和_tokenSource
并存在信号量StartAsync
并覆盖_clientWebSocket
和_tokenSource
StopAsync
调用StopAsync
,这将停止由Thread 2创建的ClientWebSocket
,但没有人会关闭由Thread 1创建的
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.