2011-01-23 37 views
5

Tôi có một vòng lặp Parallel.ForEach() lấy danh sách URL và tải xuống từng URL để xử lý bổ sung. Bên ngoài vòng lặp của tôi, tôi đã khai báo biến vòng lặp và bên trong thân vòng lặp mà tôi sử dụng Interlocked.Increment(), đây là cách tốt nhất để giữ một cách "an toàn luồng" để tăng số đếm khi mỗi vòng lặp được thực hiện.Interlocked.Increment() không hoạt động theo cách tôi mong đợi trong Thư viện song song nhiệm vụ

int counter = 0; 

Parallel.ForEach(urlList, (url, state) => 
{ 
    // various code statments 

    Interlocked.Increment(ref counter); 

    Debug.WriteLine(" ......... counter: " + counter); 
}); 

Tôi đã có thể nghĩ rằng tôi sẽ thấy một cái gì đó tương tự như:

......... 1 
......... 2 
......... 3 
......... 4 
......... 5 
......... 
......... 
......... n 

Nhưng những gì tôi nhận được thay vào đó là 16 "......... 0" (điều này là bởi vì tôi có một máy tính lõi tứ kép với 8 lõi bản địa, nhưng siêu luồng được kích hoạt cho tôi tổng cộng 16 lõi). Sau đó, tôi sẽ bắt đầu thấy bộ đếm được tăng lên bình thường cho hầu hết các phần nhưng đôi khi tôi sẽ thấy các giá trị truy cập trùng lặp hoặc thậm chí ba lần trong đầu ra Gỡ lỗi.

Sử dụng Parallel.ForEach() cách tốt nhất để đếm lặp vòng lặp là gì? Cảm ơn lời khuyên nào.

+0

Giá trị trùng lặp được mong đợi. Nhưng tôi không hiểu 0s đến từ đâu. – CodesInChaos

+0

Một work-around đang sử dụng int 'myCounter = Interloced.Increment (ref counter); Debug.WriteLine (myCounter); ' – CodesInChaos

+3

Bạn có thể đăng một số mã thực sự biên dịch và trình bày vấn đề (ví dụ: in 0s) không? – CodesInChaos

Trả lời

12

Lồng vào nhau.Giá trị gia tăng sẽ trả lại giá trị gia tăng của bạn.

Vì vậy,

int counter = 0; 

Parallel.ForEach(urlList, (url, state) => 
{ 
    // various code statments 

    var counterNow = Interloced.Increment(ref counter); 

    Debug.WriteLine(" ......... counter: " + counterNow); 
}); 

nên trả về giá trị truy cập như nó đã được tăng lên.

Edited, khá một thời gian sau:

Bằng cách giải thích:

Khi bạn đang chạy trên một máy đa bộ xử lý/đa lõi và có nhiều chủ đề ứng dụng, bạn cần phải nhận thức rằng các giá trị bạn thấy cho các biến có thể không phản ánh trạng thái hiện tại thực tế của biến đó. Điều này là do có thể có nhiều bộ đệm riêng lẻ, cho mỗi CPU hoặc chết hoặc ổ cắm và chuỗi của bạn có thể đọc giá trị được lưu trong bộ nhớ cache (lưu một lần truy cập để đọc bộ nhớ chính)

Nếu bạn chỉ muốn đọc một giá trị được cập nhật bởi nhiều chủ đề, bạn nên sử dụng Interlocked.Read() để đảm bảo bạn có giá trị hiện tại cho counter.

+0

Điều đó đã xảy ra. Nhờ ông Hughes và CodeInChaos ở trên cho cùng một gợi ý. – BonanzaDriver

7

Điều này là do gia tăng + Dòng ghi cùng nhau không phải là nguyên tử.
Nó có thể là số gia số 1 counter, sau đó luồng 2 lại tăng lên một lần nữa, và sau đó hai luồng sẽ đến phần WriteLine với cùng giá trị counter.

+1

Nhưng tại sao 'counter' lại được viết là' 0'? 'WriteLine()' sẽ chỉ xảy ra sau ít nhất một cuộc gọi 'Interlocked.Increment()'. –

+0

cố gắng tuyên bố truy cập là dễ bay hơi. –

+1

Lồng vào nhau đã ngụ ý một hàng rào mem nên dễ bay hơi không tạo nên sự khác biệt. –

1

Tôi không chắc chắn, nhưng tôi nghĩ rằng điều này có thể là do cách hoạt động của các biến chụp trong lambdas. Bạn đã thử đặt nó trong một chức năng riêng biệt, hoặc di chuyển biến bên ngoài và tuyên bố nó là tĩnh?

Các vấn đề liên quan