更新ConcurrentDictionary中的值字段

rbo*_*man 12 c# concurrency .net-4.0

我正在尝试更新ConcurrentDictionary中的条目,如下所示:

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上我需要遍历字典并更新每个Value上的字段.我从文档中了解到我需要避免使用Value属性.相反,我认为我需要使用TryUpdate,除了我不想替换我的整个对象.相反,我想更新对象上的字段.

在PFX团队博客上阅读此博客条目之后:也许我需要使用AddOrUpdate并且在添加委托中什么也不做.

有没有人对如何做到这一点有任何见解?


我在字典中有成千上万的对象,我需要每30秒左右更新一次.创建新的以更新属性可能是不可行的.我需要克隆现有对象,更新它并替换字典中的对象.我还需要在克隆/添加周期的持续时间内锁定它.呸.

我想做的是迭代对象并尽可能直接更新Counter属性.

我的最新研究让我看到了Parallel.ForEach,这听起来很棒,但不应该用于更新状态的动作.

我也看到了Interlocked.Increment的提及听起来不错,但我仍然需要弄清楚如何以线程安全的方式在我的字典中的每个元素上使用它.

Jas*_*dez 12

首先,解决您的锁定问题:

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.
    }
}
Run Code Online (Sandbox Code Playgroud)

迭代正确值应该比迭代键值对更快.[虽然我认为在大多数情况下迭代一系列键并进行查看会更快地在ConcurrentDictionary上.]

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() );
    }

}
Run Code Online (Sandbox Code Playgroud)


dtb*_*dtb 5

ConcurrentDictionary 不能帮助您同时访问存储值的成员,而只能访问元素本身。

如果多个线程调用 TestIt,您应该获取集合的快照并锁定共享资源(它们是各个字典值):

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

但是,如果您想更新特定键的计数器,ConcurrentDictionary 可以帮助您在键不存在时自动添加新的键值对:

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

AddOrUpdate 和 TryUpdate 确实适用于您想要替换 ConcurrentDictionary 中给定键的值的情况。但是,正如您所说,您不想更改该值,而是想更改该值的属性。