c# - 防止 web 套接字消费者代码被执行两次

标签 c# websocket

想象一下,我们使用一个带有真正高速金融数据的网络套接字。在高峰期,网络套接字方法每秒被调用数百至数千次。

我们的网络套接字方法中有一个条件,它会不时变为真。在这种情况下,应该调用另一个方法。但只有一次。由于该方法的执行速度,确实很难防止双重执行。代码如下所示:

private readonly ConcurrentDictionary<string, bool> _inExecution = new ConcurrentDictionary<string, bool>();

private void SubscribeToSocket()
{
    _socket.Connect();

    var subscription = SocketSubscriptions.CreateSubsription(data =>
    {
        Task.Run(async () =>
        {
            // read data

            if (condition)
            {
                // call method only once
                await Execute(key);

                condition = false;
            }
        }
    }
}

private async Task Execute(string key)
{
    // Even with this statement the code calls are too fast and sometimes gets executed twice
    if (!_inExecution[key])
    {
        _inExecution[key] = true;

        // do something..
    }
}

我已经尝试通过在 Execute() 方法之前随机等待来防止双重执行。像这样:

if (condition)
{
    var rnd = new Random();
    await Task.Delay(rnd.Next(15, 115));

    // call method only once
    await Execute(key);

    condition = false;
}

但在某些特殊情况下,即使这样也会执行两次。有没有更好的方法来防止这种情况发生?

最佳答案

此处的关键竞争条件似乎是检查 _inExecution[key] 之间的竞争。和*更新 _inExecution[key] = true';多个调用者可以通过那里。有多种方法可以使这个变得健壮,但在你的情况下,经过考虑,我很确定最简单的方法就是简单地同步集合,即

    private readonly HashSet<string> _inExecution = new HashSet<string>();
    private async Task Execute(string key)
    {
        // Even with this statement the code calls are too fast and sometimes gets executed twice
        bool haveLock = false;
        try
        {
            lock(_inExecution) { haveLock = _inExecution.Add(key); }
            if (haveLock)
            {
                // ... your code here
            }
        }
        finally
        {
            if (haveLock)
            {
                lock (_inExecution) _inExecution.Remove(key);
            }
        }
    }

您也可以使用 Dictionary<string, bool> ,而是一个 HashSet<string>在这里工作正常。 Dictionary<string, bool>不过,可以避免一些键空间开销 - 只是操纵值 - 类似于:

    private readonly Dictionary<string, bool> _inExecution = new Dictionary<string, bool>();
    private async Task Execute(string key)
    {
        // Even with this statement the code calls are too fast and sometimes gets executed twice
        bool haveLock = false;
        try
        {
            lock(_inExecution)
            {
                if (!_inExecution.TryGetValue(key, out var state) || !state)
                {   // if missing entirely, or not currently held: take it
                    haveLock = _inExecution[key] = true;
                }
            }
            if (haveLock)
            {
                // ... your code here
            }
        }
        finally
        {
            if (haveLock)
            {
                lock (_inExecution) _inExecution[key] = false;
            }
        }
    }

需要注意的重要一点是您不要保留 lock超过实际// ... your code here位 - 这会阻止所有并发执行,这不是您想要的。

如果你想整理一下,有一些方法可以使用自定义一次性用品等来构建它,但是 try/finally工作正常。

关于c# - 防止 web 套接字消费者代码被执行两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56467421/

相关文章:

javascript - 我可以在Python中通过tornado websocket发送gzip压缩数据吗?

c# - 确定属性是否为 EF Core 中的导航属性

c# - 使用 Mapsui 显示线串和轨迹点

javax.websocketclient : how to send large binary data from clientendpoint to serverendpoint

javascript - Chrome 无法连接到 websocket 服务器(操作码 -1) "handshake was canceled"

javascript - 我可以使用 python 连接到 https websocket 吗?

javascript - 为什么刷新页面后我的 WebSocket 连接会关闭?

c# - 显式使用 "new EventHandler"声明有好处吗?

c# - 获取、更改和更新副本缓存

c# - .net core 1.0.0 与 Entity Framework 核心 sqlite