![](/img/trans.png)
[英]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.