繁体   English   中英

使用 SemaphoreSlim 解决竞争条件

[英]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 ,因此您可能会在下一种情况下结束:

  1. 线程 1调用StartAsync并初始化_clientWebSocket_tokenSource并存在信号量
  2. 线程 2调用StartAsync并覆盖_clientWebSocket_tokenSource
  3. Thread 1 StopAsync调用StopAsync ,这将停止由Thread 2创建的ClientWebSocket ,但没有人会关闭由Thread 1创建的

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM