#c# #.net #multithreading #concurrency
#c# #.net #многопоточность #совпадение
Вопрос:
У меня есть пакет совпадающих объектов, и я хочу выполнить следующие действия над ним:
- перечислите все элементы с помощью фильтрации «где».
- для каждого элемента проверьте некоторые свойства и, основываясь на значениях, выполните вызов некоторого метода. После вызова метода лучше удалить предмет из сумки.
- измените значение некоторых свойств и сохраните его в пакете.
Так что в принципе мне нужно что-то вроде следующего:
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. Я не уверен, что это многое добавляет или отвечает, или даже сильно отличается от того, что было у ОП. Может быть, вы могли бы объяснить, почему вы считаете, что это решение