Наиболее эффективный способ перечисления и изменения в пакете параллельных объектов

#c# #.net #multithreading #concurrency

#c# #.net #многопоточность #совпадение

Вопрос:

У меня есть пакет совпадающих объектов, и я хочу выполнить следующие действия над ним:

  1. перечислите все элементы с помощью фильтрации «где».
  2. для каждого элемента проверьте некоторые свойства и, основываясь на значениях, выполните вызов некоторого метода. После вызова метода лучше удалить предмет из сумки.
  3. измените значение некоторых свойств и сохраните его в пакете.

Так что в принципе мне нужно что-то вроде следующего:

 foreach (var item in myBag.Where(it =gt; it.Property1 = true))  {    if (item.Property2 = true)  {  SomeMethodToReadTheItem(item);  //it's better to remove this item from the bag here, but   //there is a permeance hit, then just leave it.  }  else  {  item.Property3= "new value";  //now how do I save the item back to the bag?  }    }  

Конечно, это должно быть сделано потокобезопасным способом. Я знаю, что перечисление по пакету совпадений на самом деле является «моментальным снимком» реального пакета, но как насчет фильтра предложения where? Должен ли я сделать список, чтобы он не создавал новый «снимок»? Кроме того, если вы хотите изменить один конкретный предмет, вы просто кладете его в сумку.TryTake(выходящий предмет). Но поскольку я уже получил элемент в перечислении, должен ли я «взять» его снова?

Любое объяснение/комментарий/образец будет очень абрикосовым.

Спасибо.

Комментарии:

1. Очень маловероятно, что a ConcurrentBaglt;Tgt; — лучший инструмент для всего, что вы пытаетесь сделать. Этот класс представляет собой неупорядоченную сумку с предметами и поддерживает только взятие случайных предметов по одному. Вы не можете удалить определенный элемент из этой коллекции. Это специализированная коллекция, предназначенная для смешанных сценариев «производитель-потребитель», которые на практике встречаются крайне редко.

2. Класс потокобезопасен с точки зрения внутренней согласованности, однако, поскольку он содержит ссылки, не дается никаких гарантий относительно того, что вы не выстрелите себе в ногу с общими ресурсами внутри. В большинстве случаев концептуально проще просто использовать блокировку, если у вас нет достаточно конкретных вариантов использования. Также то, что сказал Теодор

Ответ №1:

Я постараюсь ответить на конкретные части вашего вопроса, не затрагивая производительность.

Во-первых, Where метод принимает an IEnumerablelt;Tgt; в качестве своего первого параметра и сам будет выполнять итерацию по перечисляемому, которое будет вызываться GetEnumerator() один раз, поэтому вы сделаете только один снимок базового ConcurrentBag .

Во-вторых, потокобезопасность вашего кода не очень ясна, в остальной части вашего кода могут быть некоторые неявные гарантии, которые не указаны. Например, у вас есть ConcurrentBag , поэтому ваша коллекция потокобезопасна, однако вы изменяете элементы, содержащиеся в этой коллекции, без какой-либо синхронизации потоков. Если есть другой код, который выполняет тот же метод или в другом методе, который одновременно считывает/изменяет элементы ConcurrentBag , вы можете увидеть гонки данных.

Обратите внимание, что нет необходимости звонить TryTake , если у вас уже есть ссылка на элемент, так как он вернет только ту же ссылку.

Ответ №2:

Я рекомендую вам просто создать новый список и, если фильтр «ГДЕ», добавить его в этот новый список. Это выглядело бы примерно так:

 Listlt;Tgt; myNewList = new Listlt;Tgt;(); foreach (var item in myBag.Where(it =gt; it.Property1))  {  if (!item.Property2)  {  myNewList.Add(item);  }   }  

внимание на » ! »

Комментарии:

1. Я не уверен, что это многое добавляет или отвечает, или даже сильно отличается от того, что было у ОП. Может быть, вы могли бы объяснить, почему вы считаете, что это решение