Delegate.BeginInvoke 如何在 .NET 2.0 中的 BeginRead 实现中发出等待句柄的信号



以下是 .NET 2.0 中 System.IO.FileStream.BeginRead 方法的实现。

如您所见,该实现将操作传递给ReadDelegateBeginInvoke方法。

但是,在执行此操作之前,它会初始化一个AutoResetEvent,然后对其调用WaitOne

但是,我不明白ReadDelegate如何向AutoResetEvent发出信号,因为它不会提及它。

你能解释一下这是如何工作的吗?

[HostProtection(SecurityAction.LinkDemand, ExternalThreading=true)]
public virtual IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
    if (!this.CanRead)
    {
        __Error.ReadNotSupported();
    }
    Interlocked.Increment(ref this._asyncActiveCount);
    ReadDelegate delegate2 = new ReadDelegate(this.Read);
    if (this._asyncActiveEvent == null)
    {
        lock (this)
        {
            if (this._asyncActiveEvent == null)
            {
                this._asyncActiveEvent = new AutoResetEvent(true);
            }
        }
    }
    this._asyncActiveEvent.WaitOne();
    this._readDelegate = delegate2;
    return delegate2.BeginInvoke(buffer, offset, count, callback, state);
}

反编译器无法帮助您检索原始源代码中的注释。 可从SSCLI20发行版中获得 .NET 2.0。 它读起来是这样的:

       // To avoid a race with a stream's position pointer & generating race
       // conditions with internal buffer indexes in our own streams that
       // don't natively support async IO operations when there are multiple
       // async requests outstanding, we will block the application's main
       // thread if it does a second IO request until the first one completes.
       if (_asyncActiveEvent == null) {
           lock(this) {
               if (_asyncActiveEvent == null)
                   _asyncActiveEvent = new AutoResetEvent(true);
           }
       }
       bool r = _asyncActiveEvent.WaitOne();
       BCLDebug.Assert(r, "AutoResetEvent didn't get a signal when we called WaitOne!");
       BCLDebug.Assert(_readDelegate == null && _writeDelegate == null, 
           "Expected no other readers or writers!");

因此,如果_asyncActiveEvent为 null,则不会有任何其他异步 I/O 操作正在运行,因此阻止该操作没有任何意义。 因此,初始化要设置的 ARE 是非常有意的。 WaitOne() 调用会再次重置它,因此在前一个调用完成之前再次调用 BeginRead() 将阻止并避免竞争条件。 EndRead() 通过再次设置来解锁它。

可能的心理减速带是 ARE 的使用与其正常使用相反。 它仅在什么都没有发生时设置。

似乎我在问题中提到的AutoResetEvent不是用来表示ReadDelegate完成的,因为它已经通过回调委托给委托上的 BeginInvoke 方法,而是为了确保调用EndRead,从而保持完整BeginXXX的原子性,并将EndXXX操作作为一个单元。

这可以从同一类的EndRead方法的代码中推断出来。

public virtual int EndRead(IAsyncResult asyncResult)
{
    if (asyncResult == null)
    {
        throw new ArgumentNullException("asyncResult");
    }
    if (this._readDelegate == null)
    {
        throw new ArgumentException(Environment.GetResourceString("InvalidOperation_WrongAsyncResultOrEndReadCalledMultiple"));
    }
    int num = -1;
    try
    {
        num = this._readDelegate.EndInvoke(asyncResult);
    }
    finally
    {
        this._readDelegate = null;
        this._asyncActiveEvent.Set();
        this._CloseAsyncActiveEvent(Interlocked.Decrement(ref this._asyncActiveCount));
    }
    return num;
}

AutoResetEvent由标识_asyncActiveEvent表示,这是一个类级实例变量。

最新更新