Поток ConcurrentDictionary поточно и эффективно изменяет свойство с помощью значения словаря - PullRequest
0 голосов
/ 12 марта 2020

Существует довольно много сообщений о поточно-ориентированных изменениях в 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; }
}

Ответы [ 2 ]

3 голосов
/ 13 марта 2020

A одновременная коллекция является потокобезопасной в том смысле, что она всегда будет внутренне согласованной , даже если это будет стоить вам снимки из коллекции (среди прочих хитрости). Тем не менее, и это большое, однако, объекты внутри коллекции или код, который вы пишете, которые ее используют, не гарантируют, что они будут поточно-безопасными .

Я не уверен в реальной проблеме вы пытаетесь решить или многопоточность свойств, которые вы пытаетесь изменить. Тем не менее, самая безопасная ставка (и если вы не уверены), это просто использовать lock при доступе к этим свойствам .

0 голосов
/ 13 марта 2020

Это небезопасно, поэтому вы должны защищать его где-то еще.

Существует несколько шаблонов, которые будут зависеть от текущего сценария. Например, если вам нужно увеличить переменную:

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

за сценой, которую вы читаете Property1, добавив 1, и сохраните ее снова в одной строке, но не в инструкции atomi c, в середина может измениться 50 раз.

Вы можете защитить этот код с помощью блокировки внутри if, но другой разработчик может прийти через 2 года и сделать то же самое где-нибудь еще, и не осознавать, что он / она забывает о блокировке

Тогда вы можете принудительно выполнять чтение и запись внутри объекта, который блокирует, например:

publi c class CustomObject {private int _Property1;

public Property1 
{
  get 
  {
     return _Property1;
  }

  public void IncrementProperty1By (int increment) 
  { 
    lock { ... Property1++ ... }
  }

}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...