gpt4 book ai didi

c# - DisposeAsync 应该抛出后台任务异常,还是留给客户端显式观察?

转载 作者:行者123 更新时间:2023-12-05 05:49:03 26 4
gpt4 key购买 nike

我不认为这个问题与 "Proper way to deal with exceptions in DisposeAsync" 重复。

假设我的类实现了 IAsynsDisposable,因为它有一个长时间运行的后台任务,而 DisposeAsync 终止了该任务。一个熟悉的模式可能是 Completion 属性,例如 ChannelReader<T>.Completion (尽管 ChannelReader 没有实现 IAsynsDisposable)。

Completion 任务的异常传播到 DisposeAsync 之外是否被认为是一种好的做法?

这是一个完整的示例,可以将其复制/粘贴到 dotnet new console 项目中。注意 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)
{
}
}
}

是否就哪个选项更好达成共识?

最佳答案

现在,我已经确定了一个可重用的辅助类 LongRunningAsyncDisposable(here's a gist,警告:尚未测试),它允许:

  • 启动后台任务;
  • 随时调用 IAsyncDisposable.DisposeAsync 停止此任务(通过取消 token ),in a thread-safe, concurrency-friendly way ;
  • 配置 DisposeAsync 是否应该重新抛出任务的异常(DisposeAsync 将等待任务以任何一种方式完成,然后再进行清理);
  • 通过LongRunningAsyncDisposable.Completion 属性随时观察任务的状态、结果和异常。

关于c# - DisposeAsync 应该抛出后台任务异常,还是留给客户端显式观察?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70718409/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com