Как удалить один конкретный объект из ConcurrentBag?


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

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

9 80

9 ответов:

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

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

два варианта ты:

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

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

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

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

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

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

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

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

метка верна в том, что 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);
}
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);
    }

}

Это мой класс, который я использую в своих проектах. Он может удалить один элемент из ConcurrentBag, а также может удалить список элементов из bag

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);
        }
    }
}
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;
}

Как насчет:

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

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