Как удалить один конкретный объект из ConcurrentBag <>? - PullRequest
92 голосов
/ 12 июня 2010

С новым ConcurrentBag<T> в .NET 4, как удалить из него определенный, конкретный объект, когда доступны только TryTake() и TryPeek()?

Я думаю об использовании TryTake(), а затем просто добавить полученный объект обратно в список, если я не хочу удалить его, но мне кажется, что я что-то упускаю Это правильный путь?

Ответы [ 9 ]

79 голосов
/ 12 июня 2010

Краткий ответ: вы не можете сделать это простым способом.

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

Две альтернативы для вас:

  • Удалите все элементы и запомните их,пока вы не найдете тот, который хотите удалить, а затем верните остальные обратно.Обратите внимание, что если два потока попытаются сделать это одновременно, у вас будут проблемы.
  • Используйте более подходящую структуру данных, такую ​​как ConcurrentDictionary .
15 голосов
/ 12 июня 2010

Вы не можете.Это сумка, она не заказана.Когда вы положите его обратно, вы просто застрянете в бесконечном цикле.

Вы хотите сет.Вы можете эмулировать один с ConcurrentDictionary.Или HashSet, который вы защищаете с помощью замка.

4 голосов
/ 17 марта 2016

ConcurrentBag отлично подходит для работы со списком, в который можно добавлять элементы и перечислять из множества потоков, а затем выбрасывать его, как предполагает его название:)

Как сказал Марк Байерс , вы можете пересобрать новый ConcurrentBag, который не содержит элемент, который вы хотите удалить, но вы должны защитить его от попадания нескольких потоков, используя блокировку.Это одна строка:

myBag = new ConcurrentBag<Entry>(myBag.Except(new[] { removedEntry }));

Это работает и соответствует духу, для которого был разработан ConcurrentBag.

3 голосов
/ 02 сентября 2016

Метка верна в том смысле, что 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);
}
3 голосов
/ 12 июня 2010

Как вы упоминаете, TryTake() является единственным вариантом.Это также пример для MSDN .Отражатель также не показывает других скрытых внутренних методов, представляющих интерес.

1 голос
/ 19 февраля 2018
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);
    }

}
0 голосов
/ 20 февраля 2018

Это мой класс расширения, который я использую в своих проектах. Он может удалить один предмет из 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);
        }
    }
}
0 голосов
/ 24 июля 2015
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;
}
0 голосов
/ 04 июля 2013

как насчет:

bag.Where(x => x == item).Take(1);

Это работает, я не уверен, насколько эффективно ...

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