C#中推荐的方法是同时和异步地从单个文件中的多个位置读取

时间:2015-12-18 13:34:17

标签: c# multithreading asynchronous filestream

我正在使用包含数据块的只读数据文件(例如,100个演出),每个数据块大约64K。我想建立一个内存缓存来处理处理每个服务请求所需的10s-100s块读取。

基本的异步非线程安全读取如下所示:

public async Task<byte[]> Read(int id)
{
    FStream.Seek(CalcOffset(id), SeekOrigin.Begin);
    var ba = new byte[64 * 1024];
    await FStream.ReadAsync(ba, 0, ba.Length);
    return ba;
}

我无法锁定FStream以使上述线程安全(C#错误“无法在锁定语句的主体中等待”)。我不能删除await而不会丢失异步行为。我目前的解决方法是从FileStreams的缓存中读取绘图:

    private BufferBlock<FileStream> StreamRead;

    public async Task<FileStream> GetReadStream()
    {
        return await StreamRead.ReceiveAsync(TimeSpan.FromMilliseconds(-1));
    }

    public async Task ReleaseReadStream(FileStream stream)
    {
        await StreamRead.SendAsync(stream);
    }

这是构建多线程异步友好缓存的最佳方法吗?还有其他建议吗?

2 个答案:

答案 0 :(得分:2)

  

我想构建一个内存缓存

你确定吗? :)

几十年来,Windows投入了大量的工作来实现内置于操作系统的高性能文件缓存。

在某些极端情况下,您可以针对特定用例执行更高效的缓存,但绝大多数情况下,它不值得付出努力。我建议先测量一下。

  

我无法锁定FStream以使上述线程安全(C#错误&#34;无法在锁定语句的主体中等待#34;)。

     

我的问题是关于是否/如何以异步方式对FileStream执行多个并发读取

您可以使用SemaphoreSlim充当async兼容锁。语法有点尴尬,但它确实有效。

另外,我还建议查看内存映射文件。

答案 1 :(得分:1)

看起来您想要的是以某种方式查找并同时读取文件。幻想的术语是执行“原子搜索和读取”操作。

Windows和Linux支持这种确切类型的操作。在Linux上,有一个名为pread的函数,在Windows上有一个称为ReadFile的函数。剩下的就是要弄清楚这些调用的混乱情况。是的,不好玩。

我也遇到了同样的问题,因此将解决方案放入了一个库中。正在展示我的图书馆pread。它允许您同时原子地进行查找和读取,并且比锁定FileStream更快。

using pread;

using var fileStream = new FileStream("my_file.txt", FileMode.OpenOrCreate);

var data = new byte[123];

var bytesWritten = P.Write(fileStream, (ReadOnlySpan<byte>)data, fileOffset: 0);
var bytesRead = P.Read(fileStream, (Span<byte>)data, fileOffset: 0);