views:

278

answers:

1

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

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

class Test
{
    private ConcurrentDictionary<int, Class1> dict =
        new ConcurrentDictionary<int, Class1>();

    public void TestIt()
    {
        foreach (var foo in dict)
        {
            foo.Value.Counter = foo.Value.Counter + 1; // Simplified example
        }
    }
}

Essentially I need to iterate over the dictionary and update a field on each Value. I understand from the documentation that I need to avoid using the Value property. Instead I think I need to use TryUpdate except that I don’t want to replace my whole object. Instead, I want to update a field on the object.

After reading this blog entry on the PFX team blog: Perhaps I need to use AddOrUpdate and simply do nothing in the add delegate.

Does anyone have any insight as to how to do this?


I have tens of thousands of objects in the dictionary which I need to update every thirty seconds or so. Creating new ones in order to update the property is probably not feasible. I would need to clone the existing object, update it and replace the one in the dictionary. I’d also need to lock it for the duration of the clone/add cycle. Yuck.

What I’d like to do is iterate over the objects and update the Counter property directly if possible.

My latest research has led me to to Parallel.ForEach which sounds great but it is not supposed to be used for actions that update state.

I also saw mention of Interlocked.Increment which sounds great but I still need to figure out how to use it on each element in my dictionary in a thread safe way.

A: 

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.

dtb
Another solution from the folks at MS was posted here: http://social.msdn.microsoft.com/Forums/en-US/parallelextensions/thread/64f0c314-e3eb-457a-9dad-14d13cf06d68I'll try both your suggestions and theirs and report back. Thank you again.
rboarman