C# 什么';s c锁和x27;当锁定ConcurrentDictionary时,如何实现条件非阻塞锁?
我的挑战:C# 什么';s c锁和x27;当锁定ConcurrentDictionary时,如何实现条件非阻塞锁?,c#,multithreading,locking,C#,Multithreading,Locking,我的挑战: 避免在由多个用户针对多个ID从UI触发的方法DoWorkWithId中出现争用条件,例如lock 允许不同id重新进入DoWorkWithId,但不允许相同id重新进入,例如使用ConcurrentDictionary,其中id是键,值是对象 应该是非阻塞锁,以便线程跳过关键部分而不等待并让其他人知道它已经在运行(至少在第二次调用时正在运行),例如监视器.TryEnter或联锁。* 我的尝试: 我猜是1。二,。可以使用ConcurrentDictionary和lock private
DoWorkWithId
中出现争用条件,例如lock
DoWorkWithId
,但不允许相同id重新进入,例如使用ConcurrentDictionary
,其中id是键,值是对象监视器.TryEnter
或联锁。*
ConcurrentDictionary
和lock
private static readonly ConcurrentDictionary<Guid, object> concurrentDictionaryMethodLock = new();
public string CallToDoWorkWithId(Guid id) // [Edited from DoWorkWithId]
{
concurrentDictionaryMethodLock.TryAdd(id, new object()); // atomic adding
lock (concurrentDictionaryMethodLock[id])
{
DoWorkWithId(id);
}
return "done";
}
3.2
为了正确地释放锁,我想我需要跟踪谁获得了锁,例如。
私有静态只读ConcurrentDictionary bools=new()代码>
所以,我试着:
private static readonly ConcurrentDictionary<Guid, object> concurrentDictionaryMethodLock = new();
private static readonly ConcurrentDictionary<Guid, bool> bools = new();
public string CallToDoWorkWithId(Guid id) // [Edited from DoWorkWithId]
{
concurrentDictionaryMethodLock.TryAdd(id, new object());
bools.TryAdd(id, false);
try
{
Monitor.TryEnter(concurrentDictionaryMethodLock[id], ref bools[id]); // error
if (bools[id])
{
DoWorkWithId(id);
}
else
{
return "Process already running. Wait, refresh page and try again.";
}
}
finally
{
if (bools[id])
{
Monitor.Exit(concurrentDictionaryMethodLock[id]);
}
}
return "done";
}
使用这种方法有任何缺陷吗?是否需要内部TryGetValue if语句?似乎你在这里混合了责任。该方法的其余部分应该只在同一时间成功地处理每个项目一次。谢谢,TryGetValue不是必需的,但会很好,这样我就可以向尝试运行该过程的其他用户显示更有意义的消息。这就是为什么我想访问这个值(用户名),而TryAdd不返回这个值,所以我想我需要它来访问这个值,同时可以删除这个值对。这看起来太复杂了。为什么不在主ConcurrentDictionary
对象中存储一个布尔值,说明它是否正在使用。因此,例如,如果您的键/值对是一个guid/对象(如示例中所示),请向该对象添加一个属性,指示它是否正在使用。当有人访问该guid时,将其设置为true;当处理完成时,将其设置为false;当另一个用户尝试访问该对象时,它将显示该对象当前正在处理,并且您可以使用简单的if
条件来拒绝访问。锁定ConcurrentDictionary
似乎违反直觉。谢谢Ryan,这完全有道理。使用TryUpdate更改对象的bool类似于CompareExchange([MSDN][1]),因此它是原子的,使用起来安全。锁定ConcurrentDictionary是在我尝试执行条件锁定时出现的。[1] 你可以看看这个问题:。它包括一个带有键控监视器
实现的答案,其中包含方法输入
、尝试输入
和退出
。
var lockObj = new Object();
bool lockTaken = false;
try
{
Monitor.TryEnter(lockObj, ref lockTaken);
if (lockTaken)
{
// The critical section.
}
else
{
// The lock was not acquired.
}
}
finally
{
// Ensure that the lock is released.
if (lockTaken)
{
Monitor.Exit(lockObj);
}
}
private static readonly ConcurrentDictionary<Guid, object> concurrentDictionaryMethodLock = new();
private static readonly ConcurrentDictionary<Guid, bool> bools = new();
public string CallToDoWorkWithId(Guid id) // [Edited from DoWorkWithId]
{
concurrentDictionaryMethodLock.TryAdd(id, new object());
bools.TryAdd(id, false);
try
{
Monitor.TryEnter(concurrentDictionaryMethodLock[id], ref bools[id]); // error
if (bools[id])
{
DoWorkWithId(id);
}
else
{
return "Process already running. Wait, refresh page and try again.";
}
}
finally
{
if (bools[id])
{
Monitor.Exit(concurrentDictionaryMethodLock[id]);
}
}
return "done";
}
private static readonly ConcurrentDictionary<Guid, string> concurrentDictionaryMethodLock = new();
public string CallToDoWorkWithId(Guid id) // [Edited from DoWorkWithId]
{
var userName = GetUserName();
if (!concurrentDictionaryMethodLock.TryAdd(id, userName)) // false means unable to add, was already added, please skip and let UI know
{
if (concurrentDictionaryMethodLock.TryGetValue(id, out var value))
{
return $"Please wait. {value} started process already.";
}
else // in case id has been removed in the meantime, even TryGetOrAdd is not atomic (MSDN remarks about valueFactory)
{
return "Process was running and finished by now. Please refresh and try again.";
}
}
try
{
DoWorkWithId(id);
}
finally
{
concurrentDictionaryMethodLock.TryRemove(id, out _);
}
return "done";
}