c# - 防止 Web 套接字使用者代码被执行两次
问题描述
想象一下,我们使用具有非常快速的财务数据的 Web 套接字。在高峰时期,网络套接字方法每秒被调用数百到数千次。
我们的网络套接字方法中有一个条件不时变为真。在这种情况下,应该调用另一个方法。但只有一次。由于该方法的执行速度,很难防止重复执行。代码如下所示:
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);
}
}
}
您也可以使用 a Dictionary<string, bool>
,但 aHashSet<string>
在这里可以正常工作。ADictionary<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
工作得很好。
推荐阅读
- r - 读一个数据。在 r 中间只有一行文本的文件
- sql - 行到列 - Oracle - 不使用联合 ALL
- r - R中数组单元的平均值/总和
- laravel - Laravel Mix 和导入文件
- amazon-web-services - 启动 AWS 多节点 Ray 集群并提交简单的 python 脚本以在 conda env 中运行
- angular - Angular Reactive Forms - 重复对象
- python - pyOpenSSL 中的 PKCS#12 支持已弃用
- excel - 尝试匹配两行文本并在 excel 中返回第三行
- android - Android kotlin 日志库
- python - 从 Thread 创建的子进程中获取进程 ID