Как удалить отдельный конкретный объект из 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);

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