2010-04-02 43 views
9

.Net FileStream可以接受以下多线程调用模式吗?FileStream的同步要求(开始/结束)(读/写)

的多个线程调用这样的方法:

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? 
} 

其中AcceptResults是:

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 
    } 
} 

我很困惑,因为文件似乎我不清楚。例如,FileStream类说:

此类型的任何公共静态成员都是线程安全的。任何实例成员不保证是线程安全的。

但的BeginRead的文件似乎考虑在飞行中有多个读取请求:

同时进行多个异步请求呈现请求完成顺序不确定。

允许多个读取在飞行中吗?写?这是确保呼叫Seek和呼叫BeginRead之间的流的Position位置的适当方式吗?或者该锁需要一直保持到EndRead,因此一次只能在一次读取或写入数据?

据我所知,回调将发生在另一个线程上,我的处理statebuffer处理,允许在飞行中读取多个。

此外,有没有人知道在文档的哪里找到这些问题的答案?或者知道某人写的文章?我一直在寻找,找不到任何东西。

相关文章:

FileStream class
Seek method
BeginRead method
EndRead
IAsyncResult interface

编辑了一些新的信息

使用Reflector快速检查显示BeginRead确实将流位置捕获到每个调用状态(NativeOverlapped结构的某些字段)。看起来,EndRead并未咨询流的位置,至少没有以任何明显的方式。这显然不是确定的,因为它可能不是明显的方式,或者它可能不被底层的本地API支持。

+0

+1写得很好的问题。 – SLaks 2010-04-02 01:43:18

回答

1

是的,文件是粗略的。不幸的是,没有关于更好的文档的线索。

编辑:实际上Joe Duffy的书Concurrent Programming on Windows has Chapter 8 APM which explain async API,IAsyncResult and such as(good book and author)。这里的基本问题仍然是MSDN说实例变量不是线程安全的,因此需要适当的同步。

因此,您有多个线程在File的相同实例上启动BeginRead?然而,BeginRead页面提到了这一点:“每次调用BeginRead时都必须调用EndRead一次,在开始另一次读取之前未能结束读取过程可能导致不良行为,如死锁。”此外,您正在调用SeeF onFile对象,而其他线程可能正在执行其BeginRead回调。不安全。

+1

如果一个线程正在寻找而另一个线程正在执行其回调,为什么要重要?大概回调意味着所请求的阅读已完成,对吗?我更关心的是在BeginRead和匹配回调之间的时间内调用Seek。除非我错过了某些东西,否则上面的代码每次调用BeginRead时都会调用EndRead一次,因此当它返回的IAsyncResult为CompletedSynchronously时,BeginRead是否调用它的回调会导致一些不确定性。 – 2010-04-03 02:56:47

+0

是的,它的每个BeginRead都有一个EndRead。但是,不能保证EndRead会在另一个线程启动它的BeginRead之前被调用,该锁无法保护该场景。 – 2010-04-03 03:03:09

+0

呵呵,在这里你可能没问题,BeginRead页面会说:“在Windows上,小于64 KB的所有I/O操作将会同步完成,以获得更好的性能,异步I/O可能会影响小于64 KB的缓冲区的性能。所以你*保证所有的读取事实上都是由你的锁序列化的。 但是,如果所有线程都通过锁和同步读取进行序列化,为什么还要使用异步API呢? – 2010-04-03 03:17:27