想象一下,我们使用一个带有真正高速金融数据的网络套接字。在高峰期,网络套接字方法每秒被调用数百至数千次。
我们的网络套接字方法中有一个条件,它会不时变为真。在这种情况下,应该调用另一个方法。但只有一次。由于该方法的执行速度,确实很难防止双重执行。代码如下所示:
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/