ConcurrentDictionary.AddOrUpdate():在更新时向列表添加值

时间:2013-12-04 20:58:13

标签: c#

我正在使用ConcurrentDictionary<String,List<String>>。我想使用AddOrUpdate方法,以便如果某个键已存在列表,则该值将添加到值列表中。由于我提供给AddOrUpdate方法的函数需要返回List,我想我想这样做:

public void AddValue( String key, String value)
{
    _dictionary.AddOrUpdate( key, new List<string> { value},
        ( k, oldValue ) => UpdateValueList( oldValue, value) );
}

private List<String> UpdateValueList( List<String> list, String value)
{
    if ( !list.Contains( value) )
    {
        list.Add( value);
    }
    return list;
}

这是处理这种情况的好方法,还是我应该做些不同的事情?

2 个答案:

答案 0 :(得分:1)

鉴于您正在使用ConcurrentDictionary我假设您正在寻找线程安全的解决方案。您的解决方案不是线程安全的,因为List<T>类不是线程安全的。

要创建一个线程安全的解决方案,您需要确保同步所有类中List<T>类的访问权限,并确保您永远不要在您的课外公开List(如果您需要,那么'我需要克隆它。)

如果不了解更多关于你想要实现的目标,就很难更准确。

<强>更新

仅提供缺乏线程安全性的示例。

  • 考虑调用AddOrUpdate方法的两个线程之间的竞争条件。

  • 运行ConcurrentDictionaryaddValueFactory方法时,
  • updateValueFactory没有锁定。这是一般设计原则:在调用外部代码时不要持有锁,因为这会导致各种问题,包括重入。

  • 因此,两个线程可以使用相同的输入列表同时运行UpdateValueList方法,这不是线程安全的。

如果你在锁中包含对列表的每个访问权限,它可能会成为线程安全的,但是我需要看到你的类的其余代码才能确定。

答案 1 :(得分:1)

是的,我意识到这很老了,但是找不到一个好的答案,所以将我的解决方案放在这里以帮助他人。

给出一个ConcurrentDictionary,其中值是您要添加值的集合(列表,ConcurentBag等)。

ConcurrentDictionary<int, ConcurrentBag<int>> myDictionary = 
                    new ConcurrentDictionary<int, ConcurrentBag<int>>(); 

检查密钥是否存在,如果没有,请添加密钥并初始化您的收藏集。

                if (!myDictionary.ContainsKey(myKey))
                    {

                    myDictionary.TryAdd(            
                        key: myKey,                 
                        value: new ConcurrentBag<int>());   

                    }

现在您知道密钥在那里并且可以修改您的集合,将您的值添加到集合中。

myDictionary[myKey].Add(myValue);

带有数据的完整示例:

    static void concurentDictionaryList()
        {
        ConcurrentDictionary<int, ConcurrentBag<int>> myDictionary = new ConcurrentDictionary<int, ConcurrentBag<int>>(); //Dictionary containing a List
        int[][] mySampleValues = new int[10][];  //Collection Of sample Data
        int myKey;
        int myValue;

                                                //Build Some Sample Data         
        for (int i = 0; i < 10; i++)
            {
            mySampleValues[i] =
                Enumerable.Range(
                    start: 1,
                    count: mySampleValues.Length - i)
                    .ToArray();
            }                              


                                                    //Loop through each sample value
        for (int i = 0; i < mySampleValues.Length; i++)  
            {

            myValue =                               //Get the value I want to add to each key;
                i;                                      

                                                    //Loop though each sample value's collection
            for (int ii = 0; ii < mySampleValues[i].Length; ii++)           
                {

                myKey =
                    mySampleValues[i][ii];          //Get the value I'll use for the key

                if (!myDictionary.ContainsKey(myKey))
                    {

                    myDictionary.TryAdd(            // If the key doesn't exist in the dictionary,
                        key: myKey,                 // then add the key and initilize the List for that key
                        value: new ConcurrentBag<int>());   

                    }

                myDictionary[myKey].Add(myValue);   // Since I know the key is in the Dictionary 
                                                    // and the list has been initilized, 
                                                    // I just add the value to my list.  
                }
            }
                                
                                                    //Validate by printing
        foreach (var itemKey in myDictionary.Keys)
            {

            Debug.WriteLine(""
                +$"{itemKey.ToString().PadLeft(2, ' ')}: "   
                +$"{string.Join(separator: ", ",values: myDictionary[itemKey].ToArray().OrderBy(o=>o))}");

            }
                      

        //Printed Results:
                        // 1: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
                        // 2: 0, 1, 2, 3, 4, 5, 6, 7, 8
                        // 3: 0, 1, 2, 3, 4, 5, 6, 7
                        // 4: 0, 1, 2, 3, 4, 5, 6
                        // 5: 0, 1, 2, 3, 4, 5
                        // 6: 0, 1, 2, 3, 4
                        // 7: 0, 1, 2, 3
                        // 8: 0, 1, 2
                        // 9: 0, 1
                        //10: 0




        }

**注意:选择集合类型时,请注意该线程中已有的线程安全注释。