[英]Should IAsyncEnumerator<T>.DisposeAsync() throw if the last MoveNextAsync() task is not awaited?
[英]Should DisposeAsync throw background task exceptions, or leave it to the client to observe explicitly?
我不认为这个问题与"Proper way to deal with exceptions in DisposeAsync"重复。
假设我的 class 实现了IAsynsDisposable
,因为它有一个长时间运行的后台任务,并且DisposeAsync
终止了该任务。 一个熟悉的模式可能是Completion
属性,例如ChannelReader<T>.Completion
(尽管ChannelReader
没有实现IAsynsDisposable
)。
在DisposeAsync
之外传播Completion
任务的异常是否被认为是一种好习惯?
这是一个完整的示例,可以复制/粘贴到dotnet new console
项目中。 注意 DisposeAsync 中的await this.Completion
DisposeAsync
:
try
{
await using var service = new BackgroundService(TimeSpan.FromSeconds(2));
await Task.Delay(TimeSpan.FromSeconds(3));
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadLine();
}
class BackgroundService: IAsyncDisposable
{
public Task Completion { get; }
private CancellationTokenSource _diposalCts = new();
public BackgroundService(TimeSpan timeSpan)
{
this.Completion = Run(timeSpan);
}
public async ValueTask DisposeAsync()
{
_diposalCts.Cancel();
try
{
await this.Completion;
}
finally
{
_diposalCts.Dispose();
}
}
private async Task Run(TimeSpan timeSpan)
{
try
{
await Task.Delay(timeSpan, _diposalCts.Token);
throw new InvalidOperationException("Boo!");
}
catch (OperationCanceledException)
{
}
}
}
或者,我可以在客户端代码中显式观察service.Completion
(并在DiposeAsync
中忽略其异常以避免它们可能被抛出两次),如下所示:
try
{
await using var service = new BackgroundService(TimeSpan.FromSeconds(2));
await Task.Delay(TimeSpan.FromSeconds(3));
await service.Completion;
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadLine();
}
class BackgroundService: IAsyncDisposable
{
public Task Completion { get; }
private CancellationTokenSource _diposalCts = new();
public BackgroundService(TimeSpan timeSpan)
{
this.Completion = Run(timeSpan);
}
public async ValueTask DisposeAsync()
{
_diposalCts.Cancel();
try
{
await this.Completion;
}
catch
{
// the client should observe this.Completion
}
finally
{
_diposalCts.Dispose();
}
}
private async Task Run(TimeSpan timeSpan)
{
try
{
await Task.Delay(timeSpan, _diposalCts.Token);
throw new InvalidOperationException("Boo!");
}
catch (OperationCanceledException)
{
}
}
}
是否有关于哪个选项更好的共识?
现在,我已经确定了一个可重用的助手 class LongRunningAsyncDisposable
( 这是一个要点),它允许:
IAsyncDisposable.DisposeAsync
来停止此任务(通过取消令牌);DisposeAsync
是否应该重新抛出任务的异常(在进行清理之前, DisposeAsync
将等待任务完成);LongRunningAsyncDisposable.Completion
属性随时观察任务的状态、结果和异常;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.