2010-06-12 24 views
74

Với ConcurrentBag<T> mới trong .NET 4, làm thế nào để bạn loại bỏ một đối tượng nhất định, cụ thể khỏi nó khi chỉ TryTake()TryPeek() có sẵn?Làm thế nào để loại bỏ một đối tượng cụ thể, duy nhất khỏi một ConcurrentBag <>?

Tôi đang nghĩ đến việc sử dụng TryTake() và sau đó chỉ cần thêm đối tượng kết quả trở lại vào danh sách nếu tôi không muốn xóa nó, nhưng tôi cảm thấy mình có thể thiếu thứ gì đó. Đây có phải là cách chính xác không?

Trả lời

63

Câu trả lời ngắn gọn: bạn không thể làm điều đó một cách dễ dàng.

ConcurrentBag giữ một hàng đợi cục bộ cho mỗi chuỗi và nó chỉ nhìn vào hàng đợi của các chủ đề khác khi hàng đợi của chính nó bị trống. Nếu bạn xóa một mục và đặt nó trở lại thì mục tiếp theo bạn xóa có thể là cùng một mục một lần nữa. Không có đảm bảo rằng việc loại bỏ nhiều lần các mục và đưa chúng trở lại sẽ cho phép bạn lặp lại tất cả các mục.

Hai lựa chọn thay thế cho bạn:

  • Hủy bỏ tất cả các mục và ghi nhớ chúng, cho đến khi bạn tìm thấy một trong những bạn muốn loại bỏ, sau đó đưa những người khác trở lại sau đó. Lưu ý rằng nếu hai chủ đề cố gắng làm điều này đồng thời, bạn sẽ có vấn đề.
  • Sử dụng cấu trúc dữ liệu phù hợp hơn như ConcurrentDictionary.
+2

Đồng bộ hóaCollection cũng có thể là một thay thế phù hợp. –

+0

@ILIABROUDNO - bạn nên đặt câu trả lời đó! Đó là SO MUCH tốt hơn so với một ConcurrentDictionary kludgey khi bạn không cần một từ điển – Denis

3

Như bạn đề cập, TryTake() là tùy chọn duy nhất. Đây cũng là ví dụ trên MSDN. Reflector cho thấy không có phương pháp nội bộ ẩn nào khác được quan tâm.

13

Bạn không thể. Túi của nó, nó không được đặt hàng. Khi bạn đặt nó trở lại, bạn sẽ chỉ bị mắc kẹt trong một vòng lặp vô tận.

Bạn muốn có Tập hợp. Bạn có thể mô phỏng một với ConcurrentDictionary. Hoặc một HashSet mà bạn tự bảo vệ mình bằng khóa.

+6

Vui lòng mở rộng. Những gì bạn sẽ sử dụng như là chìa khóa trong ConcurrentDictionary cơ bản? –

+2

Vâng, tôi cho rằng khóa sẽ là loại đối tượng bạn đang cố gắng lưu trữ, và sau đó giá trị sẽ là một tập hợp của một số loại. Điều đó sẽ "mô phỏng" một 'HashSet' như ông mô tả. –

-12

thế nào về:

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

Nó hoạt động, tôi không chắc chắn như thế nào hiệu quả ...

+0

Điều này không loại bỏ bất cứ thứ gì trong túi. Vật bạn đang lấy vẫn còn trong túi. – Keith

+3

nên là "bag = new ConcurrentBag (bag.Where (x => x! = Item))" – atikot

+3

@atikot, dòng đó làm cho tôi cười – 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

Các ConcurrentBag là rất tốt để xử lý một danh sách, nơi bạn có thể thêm các mục và liệt kê từ nhiều chủ đề, sau đó cuối cùng ném nó đi vì tên của nó được đề xuất :)

As Mark Byers told, bạn có thể tạo lại một ConcurrentBag mới không chứa mục bạn muốn xóa, bu t bạn phải bảo vệ này chống lại nhiều chủ đề truy cập bằng cách sử dụng một khóa. Đây là một lớp lót:

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

Công trình này phù hợp với tinh thần mà ConcurrentBag được thiết kế.

+0

Tôi cảm thấy câu trả lời này là gây hiểu nhầm. Để rõ ràng, điều này KHÔNG cung cấp bất kỳ sự an toàn luồng nào trong thao tác Xóa mong muốn. Và đặt một khóa xung quanh nó kinda đánh bại mục đích của việc sử dụng một bộ sưu tập đồng thời. –

+0

Tôi đồng ý. Vâng, để làm rõ một chút, ConcurrentBag được thiết kế để được lấp đầy, liệt kê và bỏ đi với toàn bộ nội dung của nó khi nó được thực hiện. Bất kỳ nỗ lực nào - bao gồm mìn- để loại bỏ một mục sẽ dẫn đến một vụ hack bẩn. Ít nhất tôi đã cố gắng để cung cấp một câu trả lời, mặc dù tốt nhất là sử dụng một lớp bộ sưu tập đồng thời tốt hơn, như ConcurrentDictionary. – Larry

2

Đánh dấu là chính xác ở chỗ ConcurrentDictionary sẽ hoạt động theo cách bạn muốn. Nếu bạn vẫn muốn sử dụng một số ConcurrentBag những điều sau đây, không hiệu quả cho bạn, sẽ đưa bạn đến đó.

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

Đây là lớp mở rộng của tôi mà tôi đang sử dụng trong các dự án của tôi. Nó có thể xóa một mục khỏi ConcurrentBag và cũng có thể xóa danh sách các mục khỏi túi

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); 
     } 
    } 
} 
Các vấn đề liên quan