簡體   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