2008-09-18 22 views
7

Tôi đang xây dựng cơ sở dữ liệu đồ chơi trong C# để tìm hiểu thêm về trình biên dịch, trình tối ưu hóa và công nghệ lập chỉ mục.Tệp không đồng bộ IO trong .Net

Tôi muốn duy trì tính song song tối đa giữa các yêu cầu (ít nhất là đọc) để đưa các trang vào vùng đệm, nhưng tôi nhầm lẫn về cách tốt nhất để thực hiện điều này trong .NET.

Dưới đây là một số tùy chọn và những vấn đề tôi đã đi qua với nhau:

  1. Sử dụng System.IO.FileStreamBeginRead phương pháp

    Tuy nhiên, vị trí trong tập tin không phải là một cuộc tranh cãi để BeginRead , nó là tài sản của FileStream (được đặt thông qua phương thức Seek), vì vậy tôi chỉ có thể phát hành một yêu cầu cùng một lúc và phải khóa luồng trong thời gian. (Hoặc là tôi? Tài liệu không rõ ràng về những gì sẽ xảy ra nếu tôi chỉ giữ khóa giữa các cuộc gọi SeekBeginRead nhưng phát hành nó trước khi gọi EndRead. Có ai biết không?) Tôi biết cách thực hiện việc này, tôi không chắc chắn đó là cách tốt nhất.

  2. Dường như có một cách khác, tập trung xung quanh cấu trúc System.Threading.Overlapped và P \ Gọi hàm ReadFileEx trong kernel32.dll.

    Thật không may, có rất nhiều mẫu, đặc biệt là trong các ngôn ngữ được quản lý. Tuyến đường này (nếu nó có thể được thực hiện để làm việc ở tất cả) dường như cũng liên quan đến phương pháp ThreadPool.BindHandle và các chủ đề hoàn thành IO trong hồ bơi thread. Tôi có ấn tượng rằng đây là cách bị xử phạt đối phó với kịch bản này dưới cửa sổ, nhưng tôi không hiểu nó và tôi không thể tìm thấy một điểm vào tài liệu đó là hữu ích cho người không chủ động.

  3. Cái gì khác?

  4. Trong một nhận xét, jacob gợi ý tạo một FileStream mới cho mỗi lần đọc trong chuyến bay.

  5. Đọc toàn bộ tệp vào bộ nhớ.

    Điều này sẽ hoạt động nếu cơ sở dữ liệu nhỏ. Các codebase là nhỏ, và có rất nhiều khác không hiệu quả, nhưng cơ sở dữ liệu chính nó không phải là. Tôi cũng muốn chắc chắn rằng tôi đang làm tất cả các sổ sách kế toán cần thiết để đối phó với một cơ sở dữ liệu lớn (mà hóa ra lại là một phần lớn của sự phức tạp: phân trang, phân loại bên ngoài, ...) và tôi lo lắng nó có thể là quá dễ dàng để vô tình lừa.

Sửa

Làm rõ lý do tại sao tôi nghi ngờ với giải pháp 1: tổ chức một khóa duy nhất tất cả các cách từ BeginRead để EndRead có nghĩa là tôi cần phải chặn bất cứ ai muốn bắt đầu một đọc chỉ vì đọc khác đang được tiến hành. Điều đó cảm thấy sai, bởi vì các chủ đề bắt đầu đọc mới có thể (nói chung) để làm một số công việc nhiều hơn trước khi kết quả trở nên có sẵn. (Trên thực tế, chỉ cần viết điều này đã khiến tôi nghĩ ra một giải pháp mới, tôi đặt câu trả lời mới.)

Trả lời

3

Những gì chúng tôi đã làm là viết một lớp nhỏ xung quanh cổng hoàn thành I/O, ReadFile và trạng thái GetQueuedCompletion trong C++/CLI, sau đó gọi lại vào C# khi thao tác hoàn tất. Chúng tôi đã chọn tuyến đường này trên BeginRead và mẫu hoạt động không đồng bộ C# để cung cấp quyền kiểm soát nhiều hơn đối với các vùng đệm được sử dụng để đọc từ tệp (hoặc ổ cắm). Đây là một hiệu suất khá lớn so với cách tiếp cận được quản lý hoàn toàn, phân bổ byte mới [] trên heap với mỗi lần đọc.

Ngoài ra, có rất nhiều ví dụ C++ hoàn chỉnh hơn về việc sử dụng cổng Hoàn thành IO trên mạng liên kết

+0

Đây là một ý kiến ​​hay. Bạn cũng có thể tránh phân bổ byte mới [] s (và đập khối đối tượng lớn) bằng cách phân bổ trước chúng theo khối lớn khi bạn tạo (hoặc phát triển) vùng đệm. –

+0

Ngoài ra, bây giờ tôi không biết về GetQueuedCompletionStatus (hoặc đọc qua nó bằng cách nào đó), điều này có thể giải thích lý do tại sao những nỗ lực của tôi tại thất bại này. Đã đến lúc đọc thêm. –

5

Tôi không chắc tại sao tùy chọn 1 sẽ không hiệu quả với bạn. Hãy nhớ rằng bạn không thể có hai luồng khác nhau cố gắng sử dụng cùng một FileStream cùng một lúc - làm như vậy chắc chắn sẽ gây ra vấn đề cho bạn.BeginRead/EndRead có nghĩa là để cho mã của bạn tiếp tục thực hiện trong khi hoạt động IO có khả năng tốn kém chiếm vị trí, không cho phép một số loại truy cập đa luồng vào một tệp.

Vì vậy, tôi khuyên bạn nên tìm kiếm và sau đó thực hiện một sự bắt đầu.

+0

Đồng ý; bạn nên sử dụng một đối tượng FileStream mới cho mỗi lần đọc không đồng bộ trong chuyến bay. –

1

Điều gì sẽ xảy ra nếu bạn nạp tài nguyên (dữ liệu tệp hoặc bất kỳ thứ gì) vào bộ nhớ trước và sau đó chia sẻ nó qua các chuỗi? Vì nó là một db nhỏ. - bạn sẽ không có nhiều vấn đề để giải quyết.

+0

Điều này hoạt động trong một số trường hợp, nhưng tôi có nghĩa là "nhỏ" theo nghĩa "vài tính năng" thay vì "không có nhiều dữ liệu". –

0

Sử dụng phương pháp # 1, nhưng

  1. Khi có một request đến, hãy khóa A. Sử dụng nó để bảo vệ một hàng đợi các lời đề nghị.Nhấn đọc. Thêm nó vào hàng đợi và trả về một số kết quả async mới. Nếu điều này dẫn đến sự bổ sung đầu tiên vào hàng đợi, hãy gọi bước 2 trước khi trở về. Thả khóa A trước khi trở về.

  2. Khi đọc xong (hoặc gọi theo bước 1), hãy khóa A. Sử dụng nó để bảo vệ popping một yêu cầu đọc từ hàng đợi. Hãy khóa B. Sử dụng nó để bảo vệ trình tự Seek ->BeginRead ->EndRead. Thả khóa B. Cập nhật kết quả async được tạo bởi bước 1 cho thao tác đọc này. (Kể từ khi một hoạt động đọc xong, gọi đây là một lần nữa.)

này giải quyết vấn đề của không chặn bất kỳ chủ đề bắt đầu đọc chỉ vì đọc khác đang diễn ra, nhưng vẫn trình tự đọc để các dòng tập tin của hiện tại vị trí không bị rối tung lên.