2010-04-02 2 views
9

Mô hình sau đây của các cuộc gọi đa luồng có thể chấp nhận được .Net FileStream?Yêu cầu đồng bộ hóa cho FileStream. (Bắt đầu/Kết thúc) (Đọc/Ghi)

Một số đề gọi một phương thức như thế này:

ulong offset = whatever; // different for each thread 
byte[] buffer = new byte[8192]; 
object state = someState; // unique for each call, hence also for each thread 

lock(theFile) 
{ 
    theFile.Seek(whatever, SeekOrigin.Begin); 
    IAsyncResult result = theFile.BeginRead(buffer, 0, 8192, AcceptResults, state); 
} 

if(result.CompletedSynchronously) 
{ 
    // is it required for us to call AcceptResults ourselves in this case? 
    // or did BeginRead already call it for us, on this thread or another? 
} 

đâu AcceptResults là:

void AcceptResults(IAsyncResult result) 
{ 
    lock(theFile) 
    { 
     int bytesRead = theFile.EndRead(result); 

     // if we guarantee that the offset of the original call was at least 8192 bytes from 
     // the end of the file, and thus all 8192 bytes exist, can the FileStream read still 
     // actually read fewer bytes than that? 

     // either: 
     if(bytesRead != 8192) 
     { 
      Panic("Page read borked"); 
     } 

     // or: 
     // issue a new call to begin read, moving the offsets into the FileStream and 
     // the buffer, and decreasing the requested size of the read to whatever remains of the buffer 
    } 
} 

Tôi đang bối rối vì các tài liệu dường như không rõ ràng đối với tôi. Ví dụ: lớp FileStream cho biết:

Bất kỳ thành viên tĩnh công cộng nào thuộc loại này đều an toàn. Bất kỳ thành viên cá thể nào cũng không được bảo đảm là luồng an toàn.

Nhưng tài liệu cho BeginRead dường như chiêm nghiệm có nhiều yêu cầu đọc trong chuyến bay:

Nhiều yêu cầu không đồng bộ đồng thời làm cho yêu cầu đặt hàng hoàn thành bấp bênh.

Nhiều lần đọc có được phép bay không? Viết? Đây có phải là cách thích hợp để đảm bảo vị trí của số Position của luồng giữa cuộc gọi đến Tìm kiếm và cuộc gọi đến BeginRead không? Hay khóa đó cần phải được giữ tất cả các cách để EndRead, do đó chỉ có một lần đọc hoặc viết trong chuyến bay tại một thời điểm?

Tôi hiểu rằng việc gọi lại sẽ xảy ra trên một chuỗi khác và việc xử lý của tôi là state, buffer sẽ xử lý theo cách cho phép nhiều chuyến bay đọc.

Hơn nữa, có ai biết nơi nào trong tài liệu để tìm câu trả lời cho những câu hỏi này không? Hoặc một bài báo được viết bởi ai đó trong biết? Tôi đã tìm kiếm và không thể tìm thấy bất cứ điều gì.

tài liệu liên quan:

FileStream class
Seek method
BeginRead method
EndRead
IAsyncResult interface

Chỉnh sửa với một số thông tin mới

Kiểm tra nhanh với Reflector cho thấy BeginRead không ghi vị trí luồng vào trạng thái cho mỗi cuộc gọi (một số trường của cấu trúc NativeOverlapped). Có vẻ như EndRead không tham khảo vị trí luồng, ít nhất là không theo bất kỳ cách rõ ràng nào. Đây không phải là kết luận, rõ ràng, bởi vì nó có thể là một cách không rõ ràng hoặc nó có thể không được hỗ trợ bởi các API cơ bản.

+0

+1 Câu hỏi hay. – SLaks

Trả lời

1

Có, tài liệu sơ sài. Không có đầu mối cho các tài liệu tốt hơn, không may.

EDIT: Trên thực tế cuốn sách của Joe Duffy Lập trình đồng bộ trên Windows có Chương 8 APM giải thích API async, IAsyncResult và như vậy (sách hay và tác giả).Tuy nhiên vấn đề cơ bản ở đây là MSDN nói rằng các biến cá thể không phải là luồng an toàn, do đó cần phải đồng bộ hóa thích hợp.

Vì vậy, bạn có nhiều luồng khởi động BeginRead trên cùng một phiên bản của tệpFile? Tuy nhiên, trang BeginRead đề cập đến điều này: "EndRead phải được gọi chính xác một lần cho mọi cuộc gọi đến BeginRead. Không kết thúc quá trình đọc trước khi bắt đầu đọc khác có thể gây ra hành vi không mong muốn như bế tắc." Ngoài ra, bạn đang gọi Seek trên đối tượng theFile trong khi các luồng khác có thể đang ở giữa việc thực thi callback BeginRead của chúng. Không an toàn chút nào.

+1

Tại sao nó lại quan trọng nếu một luồng là Tìm kiếm trong khi một luồng khác đang thực thi cuộc gọi lại của nó? Có lẽ gọi lại có nghĩa là đọc được yêu cầu hoàn tất, phải không? Tôi quan tâm nhiều hơn đến việc gọi Seek trong thời gian giữa BeginRead và cuộc gọi lại tương ứng. Trừ khi tôi bị thiếu một cái gì đó, mã ở trên gọi EndRead chính xác một lần cho mọi cuộc gọi đến BeginRead, điều chỉnh một số sự không chắc chắn về việc liệu BeginRead có gọi lại hàm gọi lại của nó khi IAsyncResult nó trả về là CompletedSynchronously hay không. –

+0

Có nó có một EndRead cho mỗi BeginRead. Tuy nhiên, không có gì đảm bảo rằng EndRead sẽ được gọi trước khi một thread khác bắt đầu BeginRead của nó, khóa không bảo vệ cho kịch bản đó. –

+0

Ồ, bạn có thể OK ở đây, trang BeginRead nói "Trên Windows, tất cả các hoạt động I/O nhỏ hơn 64 KB sẽ hoàn thành đồng bộ để có hiệu suất tốt hơn. I/O không đồng bộ có thể cản trở hiệu suất cho kích thước bộ đệm nhỏ hơn 64 KB." vì vậy bạn * đang * đảm bảo rằng tất cả các lần đọc trên thực tế được sắp xếp theo khóa của bạn. Nhưng nếu tất cả các chủ đề được sắp xếp theo thứ tự thông qua các khóa và các lần đọc đồng bộ, tại sao lại bận tâm sử dụng API async? –