【问题标题】:ConcurrentDictionary thread safe and efficient change of a property witihin a dictionary valueConcurrentDictionary 线程安全有效地更改字典值内的属性
【发布时间】:2020-03-19 03:01:29
【问题描述】:

有很多关于对 ConcurrentDictionary 进行线程安全更改的帖子,但是,我搜索的所有示例都与更改整个值有关。我认为这是一个稍微不同的问题..

我正在寻求有关更改已在 ConcurrentDictionary 中的对象值的属性的最佳方法的一些指导?

例如,我可以执行以下操作,但不确定它是否是线程安全的

CustomObject obj;
if (customObjectDictionary.TryGetValue(objectKeyToChangeProperty, out obj))
{
    obj.Property2 = "NewData";
}

另一种方法是使用 ToArray 复制字典,然后获取所需的对象,修改属性,然后使用线程安全的 AddOrUpdate 方法

obj = customObjectDictionary.ToArray().Select(x => x.Value).FirstOrDefault();
if(obj != null)
{
   obj.Property2 = "NewData";
   customObjectDictionary.AddOrUpdate(obj.Key, obj, (oldkey, oldvalue) => obj);
}

这样做似乎有点冗长,并且不确定如果多次这样做,调用 ToArray 是否会高效。

示例代码如下:

public partial class Form1 : Form
{
    private ConcurrentDictionary<int, CustomObject> customObjectDictionary { get; set; }
    public Form1()
    {
        InitializeComponent();
        InitialzeObjects();
        Start();
    }

    private void InitialzeObjects()
    {
        customObjectDictionary = new ConcurrentDictionary<int, CustomObject>();

        var o1 = new CustomObject() { Key = 1, Property1 = 1, Property2 = "Object1" };
        customObjectDictionary.AddOrUpdate(o1.Key, o1, (oldkey, oldvalue) => o1);

        var o2 = new CustomObject() { Key = 2, Property1 = 2, Property2 = "Object2" };
        customObjectDictionary.AddOrUpdate(o2.Key, o2, (oldkey, oldvalue) => o2);
    }

    private async void Start()
    {
        bool complete = await Task.Run(() => Test());
    }

    private async Task<bool> Test()
    {

        int objectKeyToChangeProperty = 2;
        CustomObject obj;

        // Method 1 change local variable directly
        if (customObjectDictionary.TryGetValue(objectKeyToChangeProperty, out obj))
        {
            obj.Property2 = "NewData";
        }

        // Method 2 - make copy first then 
        obj = customObjectDictionary.ToArray().Select(x => x.Value).FirstOrDefault();
        if(obj != null)
        {
            obj.Property2 = "NewData";
            customObjectDictionary.AddOrUpdate(obj.Key, obj, (oldkey, oldvalue) => obj);
        }

        return true;

    }
}

public class CustomObject
{
    public int Key { get; set; }
    public int Property1 { get; set; }
    public string Property2 { get; set; }
}

【问题讨论】:

    标签: c#


    【解决方案1】:

    一个并发集合线程安全的,因为它总是内部一致,即使它的代价是你得到集合的快照(以及其他诡计)。然而,它很大,集合内的对象或您编写的使用它的代码不能保证是线程安全的

    我不确定您要解决的实际问题或您要更改的属性的线程性质。不过,最安全的选择(如果您不确定)是在访问这些 属性 时只使用lock

    【讨论】:

      【解决方案2】:

      它不安全,所以你必须在其他地方保护它。

      有多种模式,具体取决于当前情况。 例如,如果您需要增加一个变量:

          if (customObjectDictionary.TryGetValue(objectKeyToChangeProperty, out obj))
          {
              obj.Property1++;
          }
      

      幕后你正在读取Property1,加1,并在一行中再次保存,但不是原子指令,中间可能会更改50次。

      您可以在 if 中使用锁来保护代码,但另一个开发人员可以在 2 年后出现并在其他地方做同样的事情,而不会意识到他/她忘记了锁

      然后您可以在锁定的对象内强制执行读写操作,例如:

      公共类 CustomObject { 私有 int _Property1;

      public Property1 
      {
        get 
        {
           return _Property1;
        }
      
        public void IncrementProperty1By (int increment) 
        { 
          lock { ... Property1++ ... }
        }
      

      }

      【讨论】:

      • 对于用 -1 标记此内容的人,我将不胜感激评论为什么我也可以学习!谢谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多