2012-03-28 20 views
8

Tôi đang cố gắng tạo một thư mục và sao chép một tệp (pdf) bên trong một số Parallel.ForEach.File.Copy in Parallel.ForEach

Dưới đây là một ví dụ đơn giản:

private static void CreateFolderAndCopyFile(int index) 
    { 
     const string sourcePdfPath = "c:\\testdata\\test.pdf"; 
     const string rootPath = "c:\\testdata"; 

     string folderDirName = string.Format("Data{0}", string.Format("{0:00000000}", index)); 

     string folderDirPath = rootPath + @"\" + folderDirName; 

     Directory.CreateDirectory(folderDirPath); 

     string desPdfPath = folderDirPath + @"\" + "test.pdf"; 

     File.Copy(sourcePdfPath, desPdfPath, true); 

    } 

Phương pháp trên sẽ tạo ra một thư mục mới và sao chép tập tin pdf sang một thư mục mới. Nó tạo ra cây thư mục này:

TESTDATA 
    -Data00000000 
     -test.pdf 
    -Data00000001 
     -test.pdf 
.... 
    -Data0000000N 
     -test.pdf 

tôi đã cố gắng gọi phương thức CreateFolderAndCopyFile trong một vòng lặp Parallel.ForEach.

private static void Func<T>(IEnumerable<T> docs) 
    { 
     int index = 0; 
     Parallel.ForEach(docs, doc => 
            { 
             CreateFolderAndCopyFile(index); 
             index++; 
            }); 
    } 

Khi tôi chạy mã này nó kết thúc với các lỗi sau:

The process cannot access the file 'c:\testdata\Data00001102\test.pdf' because it is being used by another process.

Nhưng trước tiên nó tạo ra 1111 thư mục mới và sao chép test.pdf khoảng 1111 lần trước khi tôi nhận ra lỗi này.

Điều gì đã gây ra hành vi này và cách hành vi này có thể được giải quyết?

EDITED:

Mã ở trên là mẫu đồ chơi, xin lỗi cho các chuỗi mã hóa cứng Kết luận: phương pháp song song là chậm.

Ngày mai tôi thử một số phương thức từ How to write super-fast file-streaming code in C#?.

đặc biệt: http://designingefficientsoftware.wordpress.com/2011/03/03/efficient-file-io-from-csharp/

+8

Là một sang một bên, có lẽ bạn nên sử dụng 'Path.Combine' thay vì concatenating con đường cho mình. –

+2

@ Giống như tôi nhận thấy rằng bạn chưa bỏ phiếu hoặc chấp nhận câu trả lời trên trang web. Tôi khuyên bạn nên đọc [faq] để tìm hiểu về các khía cạnh này của cộng đồng Stack Overflow. –

Trả lời

18

Bạn chưa đồng bộ hóa quyền truy cập vào index và đó có nghĩa là bạn có một cuộc chạy đua vào nó. Đó là lý do tại sao bạn có lỗi. Đối với mục đích minh họa, bạn có thể tránh cuộc đua và giữ thiết kế đặc biệt này bằng cách sử dụng Interlocked.Increment.

private static void Func<T>(IEnumerable<T> docs) 
{ 
    int index = -1; 
    Parallel.ForEach(
     docs, doc => 
     { 
      int nextIndex = Interlocked.Increment(index); 
      CreateFolderAndCopyFile(nextIndex); 
     } 
    ); 
} 

Tuy nhiên, như những người khác đề xuất, quá tải thay thế của ForEach cung cấp chỉ số vòng rõ ràng là giải pháp sạch hơn cho vấn đề cụ thể này.

Nhưng khi bạn làm việc, bạn sẽ thấy rằng các tệp sao chép là IO bị ràng buộc thay vì bộ xử lý bị ràng buộc và tôi dự đoán rằng mã song song sẽ chậm hơn mã nối tiếp.

+0

Điều này phải là một nghiên cứu quá phức tạp nhất mà tôi từng thấy. Xin chúc mừng Parallels! –

+1

@BrianGraham A 2 dòng ForEach phức tạp? –

+1

@AndrewFinnell Tôi đoán Brian đang đề cập đến việc 'index' xử lý –

6

Hoạt động gia tăng của bạn trên index là nghi ngờ ở chỗ nó không phải là luồng an toàn. Nếu bạn thay đổi hoạt động thành Console.WriteLine("{0}", index++), bạn sẽ thấy hành vi này.

Thay vào đó bạn có thể sử dụng một quá tải Parallel.ForEach có chỉ số vòng lặp:

private static void Func<T>(IEnumerable<T> docs) 
{ 
    // nb: index is 'long' not 'int' 
    Parallel.ForEach(docs, (doc, state, index) => 
          { 
           CreateFolderAndCopyFile(index); 
          }); 
} 
Các vấn đề liên quan