随机偏移的异步IO

时间:2012-05-10 22:54:47

标签: c# .net file-io asynchronous

我们有一个20 GB的文件,我们想从中随机读取数据,因此它不是顺序读取的。我打算使用异步IO,但我注意到这个限制 -

正如我所说,我的要求是随机读取偏移量。但是,BeginRead API不会在文件中占用偏移量,它只会在读取的缓冲区中占用偏移量 (http://msdn.microsoft.com/en-us/library/zxt5ahzw)

所以我唯一的选择是使用FileStream.Seek。但问题是,如果我使用的是异步IO

两个线程

使用FileStream fs = Foo.txt
Thread 1                               Thread 2

fs.Seek(offset1)                       
(Thread 1 gets preempted)              
                                       fs.Seek(offset2)        
                                       fs.BeginRead
fs.BeginRead

正如你可以看到线程1在Seek偏移1之后被抢占一样,那么线程1将最终从offset2读取而不是意图。

这是否意味着我必须使用锁?这会破坏异步IO的目的。

4 个答案:

答案 0 :(得分:2)

一点也不清楚,如果这适用于您的情况,但是给memory mapped files看看,它可能会给你一些想法。

答案 1 :(得分:1)

每个文件流都有自己的偏移量,所以这应该可行 - 请参阅下面的示例(完全同步)。

public class StackOverflow_10543252
{
    public static void Test()
    {
        byte[] bytes = Enumerable.Range(0, 256).Select(i => (byte)i).ToArray();
        File.WriteAllBytes("a.bin", bytes);
        FileStream fs1 = File.OpenRead("a.bin");
        fs1.Seek(40, SeekOrigin.Begin);
        FileStream fs2 = File.OpenRead("a.bin");
        fs2.Seek(120, SeekOrigin.Begin);
        Console.WriteLine(fs1.ReadByte()); // should be 40
        Console.WriteLine(fs2.ReadByte()); // should be 120
        fs1.Close();
        fs2.Close();
        File.Delete("a.bin");
    }
}

更新:发布此答案后看到了编辑。如果您只需要1个FileStream指针(可能需要也可能不需要),那么您需要使用一些锁定来防止两个并发操作相互重叠。但是如果你可以使用多个FileStream指针,那么你的生活会更容易。

顺便说一句,你可以使用同步调用进行非顺序读取 - 它不必是异步的(上面的例子就是这样)。添加异步操作通常会带来额外的复杂性,因此您应该看看它是否真的需要。

答案 2 :(得分:1)

如果您不希望线程彼此等待,那么每个线程需要一个Stream ,即使这些访问是异步发生的。

现在,如果你的线程花费大部分时间做其他事情,那么你可以按需打开流并在完成后关闭它。 Streams包装了本机资源,文件句柄,所以你当然不应该有数百个它们四处闲置,什么都不做。

最后一个选项是管理一个开放流池。每当线程需要从文件中读取时,将其中一个流分发给该线程。完成后,应将流返回池中以供其他线程使用。您当然必须同步对池的访问

答案 3 :(得分:0)

您可以使用“共享文件”访问选项进行读取。

如果您不需要读/写共享而只是阅读,则可以减少FileAccess / FileShare属性。

Using f As FileStream = mTransferFile.Open(FileMode.OpenOrCreate, 
    FileAccess.ReadWrite, FileShare.ReadWrite)
    Dim newPosition As Long = t.ID * mTransferInfo.BlockSize
    f.Position = newPosition

    'Do stuff here. Open another filestream etc.

End Using