我正在编写一个类,它公开了一个流的一个子部分以供读取。由于可以同时从多个不同的子段读取数据,因此一次只能有一个操作处于活动状态。
我有了在每次操作之前锁定底层流的想法。围绕BeginRead调用锁定流是否足以确保从底层流中的不同位置正确地进行并发异步读取?
public sealed class SubStream : Stream
{
// ...
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count,
AsyncCallback callback, object state)
{
lock (this.baseStream)
{
this.baseStream.Seek(this.offset + this.position, SeekOrigin.Begin);
return this.baseStream.BeginRead(buffer, offset, count,
callback, state);
}
}
public override int EndRead(IAsyncResult asyncResult)
{
int read;
lock (this.baseStream)
{
read = baseStream.EndRead(asyncResult);
this.position += read;
}
return read;
}
// Read() and ReadByte() also lock on this.baseStream (not shown).
// ...
}例如,如果线程A调用BeginRead,则获取基流上的锁。现在线程B调用BeginRead,并且必须等待锁被释放。线程A设置基流的位置并开始异步读取操作。然后释放锁。然后,线程B获取锁并更改基流的位置,并开始另一个异步读取操作。然后,过一段时间,线程A的异步读操作完成。我能确定这是从基流中的原始位置读取的吗?如果没有,我该如何修复它?
发布于 2014-04-01 16:38:08
在这里,您可能会遇到多个线程在同一资源实例(baseStream)上调用BeginRead。根据MSDN的说法,“每次对BeginRead的调用都必须恰好调用一次EndRead。在开始另一次读取之前,如果不能结束一个读取过程,可能会导致诸如死锁之类的不良行为。”在你的例子中,如果Thread B在Seek上(在baseStream上),而Thread A正在执行他们的EndRead(callback),我就会遇到麻烦。
由于需求的性质,您最好使用同步I/O包装多线程访问。这意味着,可以使用同步I/O而不是异步I/O来修改当前实现。此外,您可能需要考虑使用Monitor.WaitOne (baseStream)和Monitor.Pulse(baseStream)或Monitor.PulseAll(baseStream)通知队列线程前一个线程的完成。
或者,我想抛出分段样式的Memory-Mapped文件的另一个想法。
发布于 2014-04-01 20:13:57
在给定的代码片段中,您将从同一位置多次读取。将位置更新移动到BeginRead函数。除此之外,您还遵守了FileStream类的约定,从不并发调用该类的方法。
https://stackoverflow.com/questions/22779526
复制相似问题