2013-04-01 30 views
5

Tôi có một số mã lặp qua danh sách các bản ghi, bắt đầu tác vụ xuất cho mỗi bản ghi và tăng số lượt truy cập tiến trình lên 1 lần mỗi khi tác vụ kết thúc để người dùng biết cách xử lý quá xa.Làm cách nào để theo dõi số lượng tác vụ không đồng bộ đã hoàn thành trong vòng lặp?

Nhưng tùy thuộc vào thời gian của vòng của tôi, tôi thường thấy đầu ra hiển thị số cao hơn trước số thấp hơn.

Ví dụ, tôi sẽ mong đợi để xem kết quả như thế này:

 
Exporting A 
Exporting B 
Exporting C 
Exporting D 
Exporting E 
Finished 1/5 
Finished 2/5 
Finished 3/5 
Finished 4/5 
Finished 5/5 

Nhưng thay vào đó tôi nhận được kết quả như thế này

 
Exporting A 
Exporting B 
Exporting C 
Exporting D 
Exporting E 
Finished 1/5 
Finished 2/5 
Finished 5/5 
Finished 4/5 
Finished 3/5 

Tôi không mong đợi đầu ra để được chính xác kể từ khi tôi m không khóa giá trị khi tôi cập nhật/sử dụng nó (đôi khi nó xuất ra cùng một số hai lần, hoặc bỏ qua một số), tuy nhiên tôi sẽ không mong đợi nó để đi ngược.

kiểm tra tập hợp dữ liệu của tôi là 72 giá trị, và mã liên quan như sau:

var tasks = new List<Task>(); 
int counter = 0; 

StatusMessage = string.Format("Exporting 0/{0}", count); 

foreach (var value in myValues) 
{ 
    var valueParam = value; 

    // Create async task, start it, and store the task in a list 
    // so we can wait for all tasks to finish at the end 
    tasks.Add(
     Task.Factory.StartNew(() => 
     { 
      Debug.WriteLine("Exporting " + valueParam); 

      System.Threading.Thread.Sleep(500); 
      counter++; 
      StatusMessage = string.Format("Exporting {0}/{1}", counter, count); 

      Debug.WriteLine("Finished " + counter.ToString()); 
     }) 
    ); 
} 

// Begin async task to wait for all tasks to finish and update output 
Task.Factory.StartNew(() => 
{ 
    Task.WaitAll(tasks.ToArray()); 
    StatusMessage = "Finished"; 
}); 

Sản lượng có thể xuất hiện ngược trong cả những điều khoản debug và StatusMessage đầu ra.

Cách chính xác để đếm số lượng tác vụ không đồng bộ trong vòng lặp được hoàn thành để vấn đề này không xảy ra?

+1

Bạn có chắc chắn rằng 'Async' Prcess được đảm bảo đầu ra trong một' tăng dần Order' Dựa trên những gì tôi Tôi không nghĩ rằng nó sẽ trừ khi bạn có thể làm một 'Sort' trên' List ' – MethodMan

+0

Vì bạn đang bắt đầu tất cả các nhiệm vụ cơ bản cùng một lúc, chúng sẽ kết thúc theo một thứ tự ngẫu nhiên. Ngay cả khi bạn sử dụng Interlocked.Increment "Finished n/5" sẽ không đơn điệu. – Phil

+0

@DJKRAZE Biến 'counter' không thực sự được tăng lên cho đến khi quá trình xuất hoàn tất (Thay thế bằng' Thread.Sleep' cho mục đích thử nghiệm). Xuất khẩu khác nhau mất nhiều thời gian khác nhau để hoàn thành tùy thuộc vào bao nhiêu dữ liệu mà chúng chứa, vì vậy tôi không muốn giá trị được xác định trước khi bắt đầu tác vụ không đồng bộ. Thay vào đó, tôi đang cố gắng sử dụng biến được chia sẻ được tăng thêm 1 bất kỳ lúc nào mọi tác vụ xuất hoàn tất. – Rachel

Trả lời

7

Bạn nhận được kết quả hỗn hợp, vì truy cập không tăng lên theo thứ tự giống như Debug.WriteLine(...) phương pháp là Thực thi.

Để có được một báo cáo tiến độ phù hợp, bạn có thể giới thiệu một khóa báo cáo thành nhiệm vụ

tasks.Add(
    Task.Factory.StartNew(() => 
    { 
     Debug.WriteLine("Exporting " + valueParam); 

     System.Threading.Thread.Sleep(500); 
     lock(progressReportLock) 
     { 
      counter++; 
      StatusMessage = string.Format("Exporting {0}/{1}", counter, count); 
      Debug.WriteLine("Finished " + counter.ToString()); 
     } 
    }) 
); 
+0

@svick Yikes, bạn hoàn toàn đúng. – IdeaHat

+0

Tôi nghi ngờ điều này có thể xảy ra, cảm ơn bạn. Tôi đã chạy một vài bài kiểm tra bằng cách sử dụng một 'khóa' và có vẻ như đã xuất hiện chính xác ngay bây giờ – Rachel

5

Trong mẫu này, biến số counter thể hiện trạng thái chia sẻ giữa một số luồng. Sử dụng toán tử ++ trên trạng thái được chia sẻ chỉ đơn giản là không an toàn và sẽ cho bạn kết quả không chính xác. Nó chủ yếu nắm để các hướng dẫn sau đây

  1. đẩy ngược lại ngăn xếp
  2. push 1 để ngăn xếp
  3. thêm giá trị trên stack
  4. cửa hàng vào quầy

Bởi vì nhiều luồng đang thực hiện tuyên bố này có thể cho một người làm gián đoạn phần khác thông qua việc hoàn thành trình tự trên. Điều này sẽ làm cho giá trị không chính xác kết thúc trong counter.

Thay vì ++ sử dụng câu lệnh sau

Interlocked.Increment(ref counter); 

hoạt động này được thiết kế đặc biệt để cập nhật trạng thái có thể được chia sẻ giữa nhiều chủ đề. Liên kết sẽ xảy ra một cách nguyên tử và sẽ không bị các điều kiện chủng tộc mà tôi nêu ra

Màn hình hiển thị giá trị thực tế không có vấn đề tương tự ngay cả sau khi tôi sửa lỗi được đề xuất. Hoạt động gia tăng và hiển thị không phải là nguyên tử và do đó một luồng có thể làm gián đoạn hoạt động khác giữa tăng và hiển thị. Nếu bạn muốn các thao tác không bị gián đoạn bởi các luồng khác thì bạn sẽ cần sử dụng khóa.

object lockTarget = new object(); 
int counter = 0; 

... 

lock (lockTarget) { 
    counter++; 
    StatusMessage = string.Format("Exporting {0}/{1}", counter, count); 
    Debug.WriteLine("Finished " + counter.ToString()); 
} 

Lưu ý rằng vì thặng dư của counter nay xảy ra bên trong khóa không còn nhu cầu sử dụng Interlocked.Increment

+2

Trông giống như sử dụng Interlocked.Increment sẽ không giúp đỡ với các báo cáo trạng thái không theo thứ tự mà cô ấy nhận được. – alex

+0

@alex là chính xác, sử dụng 'Interlocked.Increment' vẫn cho kết quả đầu ra sai trật tự – Rachel

+0

@alex được cập nhật để sửa phần đó – JaredPar

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