[英]Should IAsyncEnumerator<T>.DisposeAsync() throw if the last MoveNextAsync() task is not awaited?
Why does the UseEventsFail
throw in the code below?为什么
UseEventsFail
在下面的代码中抛出? Could it be that I dispose the async enumerator without awaiting the last MoveNextAsync()
task?可能是我在不等待最后一个
MoveNextAsync()
任务的情况下处理了异步枚举器吗? This example is simplified repro of my real program, so I need to dispose the async enumerator to release its resources.这个例子是我真实程序的简化复制,所以我需要处理异步枚举器来释放它的资源。 And
Task.CompletedTask
is usually a Task.Delay()
used as a timeout for UseEvents()
.而
Task.CompletedTask
通常是一个Task.Delay()
用作UseEvents()
的超时。 If the enumerator task completes before the timeout task, then no exception is thrown.如果枚举器任务在超时任务之前完成,则不会引发异常。
Stack trace of exception:异常的堆栈跟踪:
at Program.<<<Main>$>g__GenerateEvents|0_3>d.System.IAsyncDisposable.DisposeAsync()
Code:代码:
// 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;
}
}
From the MSDN article Iterating with Async Enumerables in C# 8 by Stephen Toub:从 MSDN 文章Iterating with Async Enumerables in C# 8 by Stephen Toub:
It should be evident that it's fine for one
MoveNextAsync
call to occur on a different thread from a previous or subsequentMoveNextAsync
call;很明显,一个
MoveNextAsync
调用发生在与前一个或后续MoveNextAsync
调用不同的线程上是正常的; after all, the implementation may await a task and continue execution somewhere else.毕竟,实现可能会等待一个任务并在其他地方继续执行。 However, that doesn't mean
MoveNextAsync
is “thread-safe”—far from it.然而,这并不意味着
MoveNextAsync
是“线程安全的”——远非如此。 On a given async enumerator,MoveNextAsync
must never be invoked concurrently, meaningMoveNextAsync
shouldn't be called again on a given enumerator until the previous call to it has completed.在给定的异步枚举,
MoveNextAsync
绝不能并发调用,这意味着MoveNextAsync
不应直到它的前一个调用已完成再上一个给定的枚举调用。 Similarly,DisposeAsync
on an iterator shouldn't be invoked while eitherMoveNextAsync
orDisposeAsync
on that same enumerator is still in flight.同样,
DisposeAsync
上的迭代器不应该同时调用任何MoveNextAsync
或DisposeAsync
在同一枚举仍在飞行。
So, no, IAsyncEnumerator<T>
s do not support concurrency.所以,不,
IAsyncEnumerator<T>
不支持并发。 The same is true for IEnumerator<T>
s. IEnumerator<T>
也是如此。 You can't call Dispose
from one thread while a MoveNext
is running on another thread.当
MoveNext
在另一个线程上运行时,您不能从一个线程调用Dispose
。 Enumerating in general (synchronously or asynchronously) is not a thread-safe procedure, and must be synchronized.枚举一般(同步或异步)不是线程安全的过程,必须同步。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.