7

我正在用 C# 构建一个玩具数据库,以了解有关编译器、优化器和索引技术的更多信息。

我想在将页面带入缓冲池的(至少读取)请求之间保持最大并行度,但我对如何在 .NET 中最好地实现这一点感到困惑。

以下是一些选项以及我遇到的问题:

  1. 用途System.IO.FileStreamBeginRead方法

    但是,文件中的位置不是 的参数BeginRead,它是FileStream(通过Seek方法设置)的属性,所以我一次只能发出一个请求,并且必须在持续时间内锁定流。(或者我是吗?文档不清楚如果我只在SeekandBeginRead调用之间持有锁但在调用之前释放它会发生什么EndRead。有人知道吗?)我知道该怎么做,我只是不确定它是最好的办法。

  2. 似乎还有另一种方式,以System.Threading.Overlapped结构和 P\Invoke 为 中心ReadFileEx在 kernel32.dll 中的函数。

    不幸的是,缺乏样本,尤其是在托管语言中。这条路线(如果它可以工作的话)显然还涉及ThreadPool.BindHandle线程池中的方法和IO完成线程。我的印象是这是在 Windows 下处理这种情况的认可方式,但我不明白,我找不到对初学者有帮助的文档的入口点。

  3. 还有什么?

  4. 在评论中,雅各布建议FileStream为飞行中的每一次阅读创建一个新的。

  5. 将整个文件读入内存。

    如果数据库很小,这将起作用。代码库很小,还有很多其他的低效率,但数据库本身不是。我还想确保我正在做处理大型数据库所需的所有簿记(事实证明这是复杂性的很大一部分:分页、外部排序......)我担心它可能也是容易不小心作弊。

编辑

澄清为什么我对解决方案 1 持怀疑态度:从 BeginRead 到 EndRead 一直持有一个锁意味着我需要阻止任何想要启动读取的人,因为另一次读取正在进行中。这感觉不对,因为启动新读取的线程可能(通常)能够在结果可用之前做更多的工作。(实际上,仅仅写这篇文章就让我想到了一个新的解决方案,我把它作为一个新的答案。)

4

4 回答 4

5

我不确定我明白为什么选项 1 不适合你。请记住,您不能让两个不同的线程同时尝试使用同一个 FileStream - 这样做肯定会给您带来问题。BeginRead/EndRead 旨在让您的代码在发生潜在昂贵的 IO 操作时继续执行,而不是启用对文件的某种多线程访问。

所以我建议你先寻找然后做一个beginread。

于 2008-09-18T00:48:58.857 回答
3

我们所做的是在 C++/CLI 中围绕 I/O 完成端口、ReadFile 和 GetQueuedCompletion 状态编写一个小层,然后在操作完成时回调到 C#。我们选择了这条路线而不是 BeginRead 和 c# 异步操作模式,以提供对用于从文件(或套接字)读取的缓冲区的更多控制。与每次读取时在堆上分配新字节 [] 的纯托管方法相比,这是一个相当大的性能提升。

另外,在互联网上有更多使用 IO 完成端口的完整 C++ 示例

于 2008-09-18T01:27:41.480 回答
1

如果您首先将资源(文件数据或其他)加载到内存中,然后跨线程共享它会怎样?因为它是一个小数据库。- 你不会有那么多问题要处理。

于 2008-09-18T00:52:14.700 回答
0

使用方法#1,但是

  1. 当一个请求进来时,获取锁 A。用它来保护一个挂起的读取请求队列。将其添加到队列中并返回一些新的异步结果。如果这导致第一次添加到队列中,请在返回之前调用第 2 步。在返回之前释放锁 A。

  2. 当读取完成(或由步骤 1 调用)时,获取锁 A。使用它来保护从队列中弹出读取请求。获取锁 B。用它来保护Seek-> BeginRead->EndRead序列。释放锁 B. 更新第 1 步为此读取操作创建的异步结果。(读取操作完成后,再次调用它。)

这解决了不阻塞任何线程的问题,该线程仅仅因为另一个读取正在进行中而开始读取,但仍然对读取进行排序,以便文件流的当前位置不会被弄乱。

于 2008-09-18T01:12:55.867 回答