Как удалить отдельный конкретный объект из ConcurrentBag <>?
С новым ConcurrentBag<T>
в .NET 4, как вы удаляете из него определенный конкретный объект, когда доступны только TryTake()
и TryPeek()
?
Я думаю об использовании TryTake()
, а затем просто добавляя результирующий объект обратно в список, если я не хочу удалить его, но я чувствую, что, возможно, что-то не хватает. Правильно ли это?
Ответы
Ответ 1
Короткий ответ: вы не можете сделать это простым способом.
ConcurrentBag поддерживает локальную очередь потока для каждого потока, и он смотрит только на очереди других потоков, когда их очередь становится пустой. Если вы удалите элемент и вернете его, то следующий элемент, который вы удалите, может снова стать тем же. Нет никакой гарантии, что многократное удаление элементов и их возврат позволят вам перебирать все элементы.
Две альтернативы для вас:
- Удалите все элементы и запомните их, пока не найдете тот, который хотите удалить, а затем снова установите остальных. Обратите внимание, что если два потока попытаются сделать это одновременно, у вас будут проблемы.
- Используйте более подходящую структуру данных, такую как ConcurrentDictionary.
Ответ 2
Вы не можете. Его мешок, он не заказан. Когда вы вернете его, вы просто застрянете в бесконечном цикле.
Вам нужен набор. Вы можете эмулировать один с ConcurrentDictionary. Или HashSet, что вы защищаете себя блокировкой.
Ответ 3
ConcurrentBag отлично справляется с List, где вы можете добавлять элементы и перечислять из многих потоков, а затем, в конце концов, выбросить их, поскольку его имя предлагает:)
Как сказал Марк Байерс, вы можете перестроить новый ConcurrentBag, который не содержит элемент, который вы хотите удалить, но вы должны защитить его от нескольких потоков хиты с использованием блокировки. Это однострочный:
myBag = new ConcurrentBag<Entry>(myBag.Except(new[] { removedEntry }));
Это работает и соответствует духу, в котором был разработан ConcurrentBag.
Ответ 4
Марк правилен тем, что ConcurrentDictionary будет работать так, как вы этого хотите. Если вы хотите по-прежнему использовать ConcurrentBag, то вы не сможете понять, что вы там.
var stringToMatch = "test";
var temp = new List<string>();
var x = new ConcurrentBag<string>();
for (int i = 0; i < 10; i++)
{
x.Add(string.Format("adding{0}", i));
}
string y;
while (!x.IsEmpty)
{
x.TryTake(out y);
if(string.Equals(y, stringToMatch, StringComparison.CurrentCultureIgnoreCase))
{
break;
}
temp.Add(y);
}
foreach (var item in temp)
{
x.Add(item);
}
Ответ 5
Как вы говорите, TryTake()
- единственный вариант. Это также пример MSDN. Отражатель также не показывает других скрытых внутренних методов.
Ответ 6
public static void Remove<T>(this ConcurrentBag<T> bag, T item)
{
while (bag.Count > 0)
{
T result;
bag.TryTake(out result);
if (result.Equals(item))
{
break;
}
bag.Add(result);
}
}
Ответ 7
Попробуйте вместо этого BlockingCollection<T>
. Доступно в .Net Core.
Ответ 8
Это мой класс расширения, который я использую в своих проектах. Он может удалить один элемент из ConcurrentBag, а также удалить список элементов из сумки
public static class ConcurrentBag
{
static Object locker = new object();
public static void Clear<T>(this ConcurrentBag<T> bag)
{
bag = new ConcurrentBag<T>();
}
public static void Remove<T>(this ConcurrentBag<T> bag, List<T> itemlist)
{
try
{
lock (locker)
{
List<T> removelist = bag.ToList();
Parallel.ForEach(itemlist, currentitem => {
removelist.Remove(currentitem);
});
bag = new ConcurrentBag<T>();
Parallel.ForEach(removelist, currentitem =>
{
bag.Add(currentitem);
});
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
public static void Remove<T>(this ConcurrentBag<T> bag, T removeitem)
{
try
{
lock (locker)
{
List<T> removelist = bag.ToList();
removelist.Remove(removeitem);
bag = new ConcurrentBag<T>();
Parallel.ForEach(removelist, currentitem =>
{
bag.Add(currentitem);
});
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
Ответ 9
public static ConcurrentBag<String> RemoveItemFromConcurrentBag(ConcurrentBag<String> Array, String Item)
{
var Temp=new ConcurrentBag<String>();
Parallel.ForEach(Array, Line =>
{
if (Line != Item) Temp.Add(Line);
});
return Temp;
}
Ответ 10
как насчет:
bag.Where(x => x == item).Take(1);
Это работает, я не уверен, насколько эффективно...