2011-01-13 47 views
37

Tôi đang cố gắng đầu tiên với các nhiệm vụ mới, nhưng có điều gì đó đang xảy ra mà tôi không hiểu.Bắt đầu công việc Trong foreach Sử dụng vòng lặp Giá trị của khoản mục cuối

Đầu tiên, mã, khá thẳng về phía trước. Tôi vượt qua trong một danh sách các đường dẫn đến một số tập tin hình ảnh, và cố gắng thêm một nhiệm vụ để xử lý mỗi trong số họ:

public Boolean AddPictures(IList<string> paths) 
{ 
    Boolean result = (paths.Count > 0); 
    List<Task> tasks = new List<Task>(paths.Count); 

    foreach (string path in paths) 
    { 
     var task = Task.Factory.StartNew(() => 
      { 
       Boolean taskResult = ProcessPicture(path); 
       return taskResult; 
      }); 
     task.ContinueWith(t => result &= t.Result); 
     tasks.Add(task); 
    } 

    Task.WaitAll(tasks.ToArray()); 

    return result; 
} 

tôi đã tìm thấy rằng nếu tôi cứ để chạy này với, chẳng hạn, một danh sách của 3 đường dẫn trong một thử nghiệm đơn vị, tất cả ba nhiệm vụ sử dụng đường dẫn cuối cùng trong danh sách được cung cấp. Nếu tôi bước qua (và làm chậm quá trình xử lý vòng lặp), mỗi đường dẫn từ vòng lặp được sử dụng.

Ai đó có thể giải thích điều gì đang xảy ra và tại sao? Cách giải quyết có thể?

+3

Có thể tôi đề nghị sử dụng ReSharper này lỗi cụ thể và lỗi tiềm năng khác đang highlighten cho bạn –

Trả lời

73

Bạn đang đóng trên biến vòng lặp. Đừng làm thế. Hãy copy thay vì:

foreach (string path in paths) 
{ 
    string pathCopy = path; 
    var task = Task.Factory.StartNew(() => 
     { 
      Boolean taskResult = ProcessPicture(pathCopy); 
      return taskResult; 
     }); 
    task.ContinueWith(t => result &= t.Result); 
    tasks.Add(task); 
} 

mã hiện tại của bạn được chụp path - không phải là giá trị của nó khi bạn tạo ra các nhiệm vụ, nhưng biến riêng của mình. Biến đó thay đổi giá trị mỗi lần bạn đi qua vòng lặp - vì vậy nó có thể dễ dàng thay đổi theo thời gian mà người được ủy quyền của bạn được gọi.

Bằng cách tham gia một bản sao của biến, bạn đang giới thiệu một biến mới mỗi lần bạn đi qua vòng lặp - khi bạn chụp rằng biến, nó sẽ không được thay đổi trong phiên bản kế tiếp của vòng lặp .

Eric Lippert có một cặp bài đăng trên blog đi sâu vào chi tiết này: part 1; part 2.

Đừng cảm thấy xấu -. Này bắt hầu hết mọi người ra :(

+1

Nhưng tất nhiên Rừng cho cây và tất cả những gì.. :) –

+1

vấn đề đóng cửa này và sử dụng ngẫu nhiên Random() phải nằm trong top 5 tần số khôn ngoan tại SO – BrokenGlass

+0

Xin lưu ý rằng "lỗi" này (ban đầu * theo thiết kế *) được cho là cố định trong C# 5.0 –

12

Lambda rằng bạn đang đi qua để StartNew được tham khảo path biến, mà thay đổi trên mỗi lần lặp (ví dụ: lambda của bạn đang thực hiện việc sử dụng tài liệu tham khảo của path, chứ không chỉ giá trị của nó). Bạn có thể tạo một bản sao cục bộ của nó để bạn không chỉ đến một phiên bản sẽ thay đổi:

foreach (string path in paths) 
{ 
    var lambdaPath = path; 
    var task = Task.Factory.StartNew(() => 
     { 
      Boolean taskResult = ProcessPicture(lambdaPath); 
      return taskResult; 
     }); 
    task.ContinueWith(t => result &= t.Result); 
    tasks.Add(task); 
} 
Các vấn đề liên quan