假设我有这个简单的代码:(简化)
其中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,如果你愿意的话)
如果DB
存在,我只需要插入log
/ user/connectionID
。
好的,所以当多线程访问#4
ConcurrentDictionary
是线程安全的
但还有另一种方法是RemoveUser - 它将用户从字典中删除:
public void RemoveUser (string userName)
{
string removed;
if ( MyConCurrentDictionary.TryRemove(userName,out removed ))
Clients.All.removeClientfromChat(userName);
}
但有可能上下文将出现在行#5
中,它将从ConcurrentDictionary执行RemoveUser
和 REMOVE UserName。
因此,为了解决这个问题,代码将是这样的:
lock(locker)
{
if (MyConCurrentDictionary.TryGetValue(UserName,out ConnectionId))
{
//...
}
}
完全击败了我ConcurrentDictionary
的目的。
问题
在多线程环境中执行此类操作的正确方法是什么 - 仍然具有ConcurrentDictionary的优势?
nb,是ConcurrentDictionary仅对字典中的操作是线程安全的。但我想说的是,在特定情况下,我放弃了ConcurrentDictionary的好处,因为我仍然需要使用锁。(我可能错了)。
答案 0 :(得分:2)
根据您所写的内容和我目前所理解的内容: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();
}
}
答案 1 :(得分:1)
看起来像设计问题。
你有ConcurentDictionary
,但是你没有原子地使用它,有一个操作由几个命令组成,所以你必须使用lock
(或其他同步)。 / p>
最简单的解决方案是允许LOG
发送带有已删除用户ID的邮件,换句话说,处理此cas(使用TryGetValue
中的MyConcurentDictionary
)每当您处理LOG
条消息时。
我还可以考虑拥有2个集合:一个是您拥有有效的用户ID,另一个是已删除用户。如果要添加到LOG
,则会检查两个集合。如果要删除用户,则将该用户添加到已删除的用户集合中,并在一段时间后从有效集合中删除该用户(某些用户)时间= 1秒或其他)。