Updating fields of values in a ConcurrentDictionary

后端 未结 3 1416
执笔经年
执笔经年 2020-12-18 22:33

I am trying to update entries in a ConcurrentDictionary something like this:

class Class1
{
    public int Counter { get; set; }
}

class Test
{
    private          


        
相关标签:
3条回答
  • 2020-12-18 23:12

    First, to solve your locking problem:

    class Class1
    {
        // this must be a variable so that we can pass it by ref into Interlocked.Increment.
        private int counter;
    
        public int Counter
        {
            get{return counter; }
        }
    
        public void Increment()
        {
            // this is about as thread safe as you can get.
            // From MSDN: Increments a specified variable and stores the result, as an atomic operation.
            Interlocked.Increment(ref counter);
    
            // you can return the result of Increment if you want the new value,
            //but DO NOT set the counter to the result :[i.e. counter = Interlocked.Increment(ref counter);] This will break the atomicity.
        }
    }
    

    Iterating the just values should be faster than iterating the key value pair. [Though I think iterating a list of keys and doing the look-ups will be faster still on the ConcurrentDictionary in most situations.]

    class Test
    {
        private ConcurrentDictionary<int, Class1> dictionary = new ConcurrentDictionary<int, Class1>();
    
        public void TestIt()
        {
            foreach (var foo in dictionary.Values)
            {
                foo.Increment();
            }
        }
    
        public void TestItParallel()
        {
            Parallel.ForEach(dictionary.Values,x=>x.Increment() );
        }
    
    }
    
    0 讨论(0)
  • 2020-12-18 23:22

    ConcurrentDictionary doesn't help you with accessing members of stored values concurrently, just with the elements themselves.

    If multiple threads call TestIt, you should get a snapshot of the collection and lock the shared resources (which are the individual dictionary values):

    foreach (KeyValuePair<int, Class1> kvp in dict.ToArray())
    {
        Class1 value = kvp.Value;
        lock (value)
        {
            value.Counter = value.Counter + 1;
        }
    }
    

    However, if you want to update the counter for a specific key, ConcurrentDictionary can help you with atomically adding a new key value pair if the key does not exist:

    Class1 value = dict.GetOrAdd(42, key => new Class1());
    lock (value)
    {
        value.Counter = value.Counter + 1;
    }
    

    AddOrUpdate and TryUpdate indeed are for cases in which you want to replace the value for a given key in a ConcurrentDictionary. But, as you said, you don't want to change the value, you want to change a property of the value.

    0 讨论(0)
  • 2020-12-18 23:30

    You can use the AddOrUpdate function.

    Here is how you can increment the current value by 1:

    dict.AddOrUpdate(key, 1, (key, oldValue) => oldValue + 1);
    
    0 讨论(0)
提交回复
热议问题