ConcurrentDictionary&原子操作 - 有时需要锁吗?

时间:2014-05-19 07:43:14

标签: c# multithreading .net-4.0 concurrentdictionary

假设我有这个简单的代码:(简化)

其中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的好处,因为我仍然需要使用锁。(我可能错了)。

2 个答案:

答案 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秒或其他)。