Làm cách nào để xóa tất cả các mục khỏi ConcurrentBag?


Câu trả lời:


37

Mặc dù nó có thể không hoàn toàn rõ ràng do điều kiện chủng tộc tiềm ẩn, nhưng điều này là đủ:

while (!myBag.IsEmpty) 
{
   myBag.TryTake(out T _);
}

23
Đây chắc chắn không phải là nguyên tử. Vì vậy, tôi đoán không nên thêm vào làm phương thức mở rộng trên ConcurrentBag, vì tất cả các phương thức khác đều được sử dụng với giả định là truy cập nguyên tử.
Anupam

Ngoài ra, không thể TryTake không thành công vì những lý do khác ngoài việc nó bị trống?
BrainSlugs83

21
Điều này rất nguy hiểm, nếu một tiến trình khác đang thêm các mục liên tục thì quá trình này có thể vẫn bận.
IvoTops

4
Câu trả lời từ @Adam Houldsworth + nhận xét của tôi tốt hơn.
Chris Marisic

1
nếu đây là một giải pháp nguy hiểm, tại sao nó vẫn được chấp nhận?
Barış Akkurt

65

Cập nhật 10/03/2017: Như @Lou đã chỉ ra một cách chính xác, nhiệm vụ là nguyên tử. Trong trường hợp này, việc tạo ra ConcurrentBagwill không phải là nguyên tử, nhưng việc đặt tham chiếu đó vào biến sẽ là nguyên tử - vì vậy việc khóa hoặc Interlocked.Exchangexung quanh nó là không bắt buộc.

Một số bài đọc thêm:

phép gán tham chiếu là nguyên tử, vậy tại sao cần Interlocked.Exchange (ref Object, Object)?

Một chỉ định tham chiếu có an toàn không?


Bạn luôn có thể khóa quyền truy cập vào chính chiếc túi và tạo một phiên bản mới của nó. Sau đó, các vật phẩm trong túi sẽ có thể sử dụng được đối với GC nếu không có thứ gì khác đang giữ chúng:

lock (something)
{
    bag = new ConcurrentBag();
}

Hoặc như Lukazoid chỉ ra:

var newBag = new ConcurrentBag();
Interlocked.Exchange<ConcurrentBag>(ref bag, newBag);

Tuy nhiên, cách dễ dàng để phân loại nội dung, điều này giả định rằng bất cứ khi nào một mục muốn truy cập, nó cũng sẽ bị khóa - điều này có thể tốn kém và có thể phủ nhận việc điều chỉnh hiệu suất đã đi vào ConcurrentBagchính nó.

Nếu bạn biết rằng không có gì khác sẽ truy cập vào túi vào lúc này, hãy canh cánh và cầu nguyện nó và đừng khóa :-)


Không phải là một giải pháp tốt hơn là chỉ lấy một bản sao tham chiếu của chiếc túi ở bất kỳ nơi nào bạn sử dụng túi, sau đó bạn chỉ có thể làm gì bag = newvới sự trừng phạt?
Chris Marisic

1
@ChrisMarisic Có, nếu bạn có thể tránh chia sẻ dữ liệu thì bạn không gặp phải những vấn đề này. Tuy nhiên, câu hỏi không có nhiều ngữ cảnh.
Adam Houldsworth

10
Interlocked.Exchangecó thể tốt hơn một ổ khóa
Lukazoid

5
Làm thế nào Interlocked.Exchangehoạt động nếu một sợi khác được thêm vào túi tại thời điểm trao đổi? Điều đó Addcó bị khóa trong thời gian Exchange?
Brandon

2
Các phép gán là nguyên tử trong .NET, cả khóa và Interlocked.Exchangelà dự phòng ở đây (và không cung cấp an toàn cho luồng).
Lou

24

Câu trả lời đã chọn là một giải pháp tốt, vì vậy tôi đang thêm cách giải quyết của riêng mình.

Giải pháp của tôi là xem xét tất cả các bộ sưu tập có sẵn trong không gian tên System.Collections.Concurrent để tìm một vùng mà việc xóa tất cả các phần tử khỏi bộ sưu tập là không đáng kể.

Lớp ConcurrentStack có phương thức Clear () xóa tất cả các phần tử khỏi tập hợp. Trên thực tế, đó là bộ sưu tập duy nhất trong không gian tên (hiện tại) làm được điều đó. Có, bạn phải Push(T element)thay thế Add(T element), nhưng thành thật mà nói điều đó đáng để tiết kiệm thời gian.


1
Tuy nhiên, có rất nhiều sự khác biệt quan trọng khác giữa các bộ sưu tập của anh ấy. Ví dụ: nếu bạn cần xác định xem một món đồ nhất định có nằm trong bộ sưu tập hay không thì nó vừa tầm thường và hiệu quả với Túi, nhưng không phải với Ngăn xếp.
Servy

@Servy: Vâng nhưng vẫn còn. Không, thực ra, tôi đã bắt đầu viết một danh sách pro / con, nhưng nó thực sự phụ thuộc vào yêu cầu của bạn là gì. Ví dụ: các bộ sưu tập đồng thời rất tốt cho truy cập đa luồng, nhưng không có bộ sưu tập nào trong số chúng cho phép bạn lập chỉ mục vào bộ sưu tập, điều gì đó có thể ngăn bạn sử dụng chúng. Câu hỏi đặc biệt là về việc xóa một túi đồng thời (đó là lý do tại sao tôi tìm thấy nó) và việc xóa bỏ bộ sưu tập một cách nhỏ nhặt với độ an toàn của chuỗi là yêu cầu của tôi. Câu trả lời của tôi là chuyển đổi bộ sưu tập.

2
Tôi cũng sử dụng ngăn xếp đồng thời thay vì túi. Thật lạ là ngăn xếp có Clear và Bag thì không. Mục đích chính của túi là để lưu trữ các giá trị, kiểm tra sự tồn tại và loại bỏ tất cả hoặc đơn lẻ. Vì vậy, ngăn xếp đồng thời trở thành một cái gì đó tương tự như "túi đồng thời thực có giới hạn một chút".
Maxim

@Servy làm thế nào bạn có thể xác định một cách hiệu quả nếu một mục nhất định được chứa trong a ConcurrentBag? Tôi không thể thấy bất kỳ thuộc tính hoặc phương thức gốc nào để làm điều đó. Các Containskhông được tính. Nó là một phương pháp mở rộng cho IEnumerablecác s chung chung và nó là bất cứ thứ gì nhưng hiệu quả.
Theodor Zoulias

9

Theo tinh thần của các giải pháp thay thế .. ConcurrentDictionary<T, bool>có một Clear nguyên tử, nhưng cũng cho phép bạn nhanh chóng kiểm tra xem có tồn tại một khóa hay không. Tất nhiên, 'nhanh chóng' là một thuật ngữ tương đối, nhưng tùy thuộc vào cách sử dụng của bạn, nó có thể nhanh hơn so với việc liệt kê một ngăn xếp lớn.


Đẹp quá! Đây nên được coi là loại thùng chứa được chọn cho các trường hợp như vậy. Tương tự như ConcurrentStack.
Độ trễ


-2
int cnt = _queue.Count;
for (; cnt > 0; cnt--)
{
     _queue.TryDequeue(out img);
}

Nó không rơi vào một vòng lặp vô hạn và xóa nội dung của thời điểm hiện tại.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.