2010-06-12 3 views
74

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

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

ответ

63

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

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

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

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

SynchronizedCollection также может быть подходящей заменой. –

+0

@ILIABROUDNO - вы должны сказать это как ответ! Это намного лучше, чем kludgey ConcurrentDictionary, когда вам не нужен словарь – Denis

3

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

13

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

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

+6

. Что бы вы использовали в качестве ключа в базовом ConcurrentDictionary? –

+2

Ну, я полагаю, что ключ был бы типом объекта, который вы пытаетесь сохранить, а затем значение будет представлять собой коллекцию. Это будет «подражать» «HashSet», как он описывает. –

-12

как насчет:

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

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

+0

Это не снимает ничего с мешка. Элемент, который вы извлекаете, остается в сумке. – Keith

+3

должно быть «bag = new ConcurrentBag (bag.Where (x => x! = Item))» – atikot

+3

@atikot, эта линия заставила меня смеяться – parek

-4
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; 
} 
1

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

As Mark Byers told, вы можете перестроить новый ConcurrentBag, который не содержит элемент, который вы хотите удалить, bu t вы должны защитить это от нескольких ударов нитей с помощью блокировки. Это один вкладыш:

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

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

+0

Я чувствую, что этот ответ вводит в заблуждение. Чтобы быть ясным, это НЕ обеспечивает безопасность потока в желаемой операции удаления. И блокировка вокруг этого рода поражает цель использования параллельной коллекции. –

+0

Согласен. Ну, чтобы немного уточнить, ConcurrentBag предназначен для заполнения, перечисления и выброса всего содержимого, когда это будет сделано. Любые попытки, включая мой - удалить элемент, приведут к грязному взлому. По крайней мере, я попытался дать ответ, хотя лучше всего использовать лучший параллельный класс коллекции, например ConcurrentDictionary. – Larry

2

Правильный ответ: 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); 
} 
1
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); 
    } 

} 
2

Это мой класс расширения, которые я использую в своих проектах. Это может сделать удалить один элемент из 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); 
     } 
    } 
} 
Смежные вопросы