在ConcurrentDictionary AddOrUpdate中为更新部分添加什么


109

我正在尝试使用Dictionary重新编写一些代码以使用ConcurrentDictionary。我已经查看了一些示例,但是在实现AddOrUpdate函数时仍然遇到困难。这是原始代码:

    dynamic a = HttpContext;
    Dictionary<int, string> userDic = this.HttpContext.Application["UserSessionList"] as Dictionary<int, String>;

   if (userDic != null)
   {
      if (useDic.ContainsKey(authUser.UserId))
      {
        userDic.Remove(authUser.UserId);
      }
   }
  else
  {
     userDic = new Dictionary<int,string>();
  }
  userDic.Add(authUser.UserId, a.Session.SessionID.ToString());
  this.HttpContext.Application["UserDic"] = userDic;

我不知道要为更新部分添加什么:

userDic.AddOrUpdate(authUser.UserId,
                    a.Session.SessionID.ToString(),
                    /*** what to add here? ***/);

任何指针将不胜感激。

Answers:


220

您需要传递一个Func返回值,以在更新的情况下将其存储在字典中。我想在您的情况下(因为您不区分添加和更新),这可能是:

var sessionId = a.Session.SessionID.ToString();
userDic.AddOrUpdate(
  authUser.UserId,
  sessionId,
  (key, oldValue) => sessionId);

即,Func始终返回sessionId,以便Add和Update设置相同的值。

顺便说一句:MSDN页面上有一个示例。


4
我一直在努力寻找可以添加或更新为相同值的函数。thaks
Zapnologica 2014年

2
好答案。仅从Visual Studio中显示的AddOrUpdate()的签名中,您就只能猜测这两个参数的含义。但是在@ user438331询问的特定情况下,我认为我的答案中使用简单索引器的解决方案更好。
尼古拉斯·彼得

7
正如@NiklasPeter指出的那样(stackoverflow.com/a/32796165/8479),最好只使用常规索引器覆盖该值,因为在这种情况下,您对现有值(如果有)不感兴趣。更具可读性。
罗里

3
我建议更改您的答案以将用户指向@NiklasPeter的答案。这是一个更好的解决方案。
Will Calderwood's

63

希望我不会在您的问题中遗漏任何东西,但是为什么不这样呢?它更加简单,原子和线程安全(请参阅下文)。

userDic[authUser.UserId] = sessionId;

将键/值对无条件地存储到字典中,如果键已经存在,则覆盖该键的任何值:使用索引器的setter

(请参阅:http : //blogs.msdn.com/b/pfxteam/archive/2010/01/08/9945809.aspx

索引器也是原子的。如果您通过传递函数,则可能不是:

所有这些操作都是原子的,并且对于ConcurrentDictionary上的所有其他操作都是线程安全的。关于每个操作的原子性的唯一警告是那些接受委托(即AddOrUpdate和GetOrAdd)的操作。[...]这些委托在锁之外被调用

参见:http : //blogs.msdn.com/b/pfxteam/archive/2010/01/08/9945809.aspx


2
是原子的,因为它一次全部发生,并且不能一半发生或被中断。但是不安全,因为其他人可以在执行操作之前将其更改为其他内容,在这种情况下,更改丢失了,并且您不知道更改发生了,如果您只想在值是您期望的值时进行更改,那么这不会为你做。
蹦床

26

我最终实现了一个扩展方法:

static class ExtensionMethods
{
    // Either Add or overwrite
    public static void AddOrUpdate<K, V>(this ConcurrentDictionary<K, V> dictionary, K key, V value)
    {
        dictionary.AddOrUpdate(key, value, (oldkey, oldvalue) => value);
    }
}

1

对于那些有兴趣的人,我目前正在实施一个案例,这是使用“ oldValue”又称为现有值而不是强制使用新值的一个很好的例子(个人而言,我不喜欢“ oldValue”一词,因为事实并非如此)刚创建时就已经很旧了,只是在并行线程中的几个处理器之前。

dictionaryCacheQueues.AddOrUpdate(
    uid,
    new ConcurrentQueue<T>(),
    (existingUid, existingValue) => existingValue
);

6
如果您不想更改现有值,则应GetOrAdd()改用msdn.microsoft.com/en-us/library/ee378674(v=vs.110).aspx
罗里

1
嗯,是的,在这种情况下,GetOrAdd()更简单,足够了-提示!
尼古拉斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.