ConcurrentDictionary最后,原子操作——有时需要Lock
本文关键字:Lock 原子操作 ConcurrentDictionary 最后 | 更新日期: 2023-09-27 18:04:04
假设我有这样一个简单的代码:(simplify)
其中MyConCurrentDictionary
是静态的ConcurrentDictionary<string, string>
(它驻留在不同的类中)。
/*1*/ public void Send(string Message, string UserName)
/*2*/ {
/*3*/ string ConnectionId;
/*4*/ if (MyConCurrentDictionary.TryGetValue(UserName,out ConnectionId))
/*5*/ {
/*6*/ //...
/*7*/ DB.InsertMessage( Message, ConnectionId);
/*8*/ LOG.LogMessage ( Message, ConnectionId);
/*9*/ //...
/*10*/ }
/*11*/ else ...
/*12*/ }
此方法在多个实例中运行。(signalR hub,如果你愿意)
如果user/connectionID
存在,我需要插入到DB
/log
ONLY。
Ok,那么当多线程访问ConcurrentDictionary
时,#4
行是线程安全的
但是还有另一个方法RemoveUser -它从字典中删除用户:
public void RemoveUser (string userName)
{
string removed;
if ( MyConCurrentDictionary.TryRemove(userName,out removed ))
Clients.All.removeClientfromChat(userName);
}
但是上下文可能会发生在#5
行,这将执行RemoveUser
和从ConcurrentDictionary中删除 UserName。
为了解决这个问题-代码应该是这样的:
lock(locker)
{
if (MyConCurrentDictionary.TryGetValue(UserName,out ConnectionId))
{
//...
}
}
完全违背了 ConcurrentDictionary
的目的。
在多线程环境中做这些事情的正确方法是什么,同时仍然有ConcurrentDictionary的好处?
nb,是ConcurrentDictionary只有在字典内的操作才是线程安全的。但我想说的是,在特定的情况下,我失去了ConcurrentDictionary的好处,因为我仍然需要使用锁。(也许我错了)
根据您所写的内容和我目前的理解:ConcurrentDictionary不是合适的选择!
写一个类,封装一个普通的Dictionary,用ReaderWriterLockSlim
封装读和写操作。这比普通的锁要快得多。你也可以同时有多个读操作,但只有一个写操作。
private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
public void DoRead(string xyz)
{
try
{
_lock.EnterReadLock();
// whatever
}
finally
{
_lock.ExitReadLock();
}
}
public void DoWrite(string xyz)
{
try
{
_lock.EnterWriteLock();
// whatever
}
finally
{
_lock.ExitWriteLock();
}
}
看起来像是设计问题。
你有ConcurentDictionary
,但你不自动使用它,有一个由几个命令组成的操作,所以你必须使用lock
(或其他同步)。
最简单的解决方案是允许LOG
具有已经删除的用户id的消息,换句话说,在处理LOG
消息时处理此cas(从MyConcurentDictionary
使用TryGetValue
)。
我也可以考虑有两个集合:一个是有效的用户id,另一个是已删除的用户。当您想要添加到LOG
时,则检查两个集合。当您想要删除用户时,然后将该用户添加到已删除用户集合中,并在一段时间后从有效的集合中删除该用户(一些时间= 1秒或其他)。