使用Stream.BeginRead进行顺序异步读取

时间:2014-04-01 07:35:23

标签: c# asynchronous stream locking

我正在编写一个类,它公开了一个用于阅读的流的子部分。由于可以同时从多个不同的子部分读取数据,因此任何时候只能有一个操作处于活动状态。

我想在每次操作之前锁定底层流。锁定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的异步读取完成。我可以确定这是从基本流中的原始位置读取的吗?如果没有,我该如何解决?

2 个答案:

答案 0 :(得分:0)

在这里,您最终可能会在同一个资源实例(BeginRead)上调用baseStream多个线程。根据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文件的另一个想法。

答案 1 :(得分:0)

在给定的代码段中,您将从同一位置多次读取。将位置更新移动到BeginRead功能。除此之外,你通过永远不会同时调用它的方法来履行FileStream类的合同。