1

为什么会UseEventsFail在下面的代码中抛出?难道是我在不等待最后一个MoveNextAsync()任务的情况下处理了异步枚举器?这个例子是我真实程序的简化复制,所以我需要处理异步枚举器来释放它的资源。并且Task.CompletedTask通常Task.Delay()用作UseEvents(). 如果枚举器任务在超时任务之前完成,则不会引发异常。

异常的堆栈跟踪:

at Program.<<<Main>$>g__GenerateEvents|0_3>d.System.IAsyncDisposable.DisposeAsync()

代码:

// All these are ok
await GenerateEvents().GetAsyncEnumerator().DisposeAsync();
await using var enu = GenerateEvents().GetAsyncEnumerator();
await UseEvents();
await UseEvents2();

// This fail
await UseEventsFail();

async Task UseEvents()
{
    await using var enu = GenerateEvents().GetAsyncEnumerator();
    await Task.WhenAny(enu.MoveNextAsync().AsTask());
}

async Task UseEvents2()
{
    var enu = GenerateEvents().GetAsyncEnumerator();
    await Task.WhenAny(enu.MoveNextAsync().AsTask(), Task.CompletedTask);
}

async Task UseEventsFail()
{
    await using var enu = GenerateEvents().GetAsyncEnumerator();
    await Task.WhenAny(enu.MoveNextAsync().AsTask(), Task.CompletedTask);
}

async IAsyncEnumerable<bool> GenerateEvents()
{
    while (true) {
        await Task.Delay(1000);
        yield return true;
    }
}
4

1 回答 1

1

来自 Stephen Toub 的 MSDN 文章Iterating with Async Enumerables in C# 8

很明显,一个MoveNextAsync调用发生在与先前或后续调用不同的线程上是可以的MoveNextAsync;毕竟,实现可能会等待一个任务并在其他地方继续执行。然而,这并不意味着MoveNextAsync“线程安全”——远非如此。在给定的异步枚举器上,MoveNextAsync绝不能同时调用,这意味着MoveNextAsync在对给定枚举器的上一次调用完成之前,不应再次在给定的枚举器上调用。同样,DisposeAsync不应在同一枚举器仍在运行时调用迭代MoveNextAsyncDisposeAsync

所以,不,IAsyncEnumerator<T>s 不支持并发。s也是如此IEnumerator<T>。当 a在另一个线程上运行时,您不能Dispose从一个线程调用。MoveNext通常枚举(同步或异步)不是线程安全的过程,必须同步。

于 2021-11-05T00:57:19.583 回答