有.NET Core Hosted Service一个(后台工作人员)应用程序会无限期地运行,直到它被禁用。
标准的 .NET Core 抽象类接口BackgroundService提供了一个Task ExecuteAsync(CancellationToken stoppingToken). 我已经实现了这样的:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
_listener.Start(); // _listener инициализируется в конструкторе
while (!stoppingToken.IsCancellationRequested)
{
var client = await _listener.AcceptTcpClientAsync();
var thread = new Thread(async () => await ProcessClientAsync(client, stoppingToken));
thread.Start();
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error was occured");
}
}
当应用程序终止时,这个stoppingToken变成Cancelled. 问题是如果您await _listener.AcceptTcpClientAsync();在等待时关闭应用程序,它将崩溃并出现以下错误:
System.OperationCanceledException: '操作已取消。'
此外,这个错误是在方法之外抛出的ExecuteAsync。据我了解,这是因为当前线程仍在等待完成await _listener.AcceptTcpClientAsync();,但它并没有终止。
问题:如何包装方法调用await _listener.AcceptTcpClientAsync();,使其等待由条件完成stoppingToken.IsCancellationRequested?因此,例如,如果stoppingToken.IsCancellationRequested == true,则将引发某种类型的异常(例如,OperationCancelledException)
我看到了这样的包装,但我不确定它有多正确:
public static async Task<T> WithWaitCancellation<T>(
this Task<T> task, CancellationToken cancellationToken)
{
// The tasck completion source.
var tcs = new TaskCompletionSource<bool>();
// Register with the cancellation token.
using (cancellationToken.Register(
s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
// If the task waited on is the cancellation token...
if (task != await Task.WhenAny(task, tcs.Task))
throw new OperationCanceledException(cancellationToken);
// Wait for one or the other to complete.
return await task;
}
一般来说,没办法。是的,您可以摆脱异常,但主要问题是程序的一部分甚至不考虑结束,它必须被超时中断。
在特定情况下,
TcpListener可以简单地停止它:一般来说,我们不应该忘记方法
Register,它和属性一样有用IsCancellationRequested,如果不是更有用的话。PS如果您正在编写异步代码 - 为什么要通过 Thread.Start 创建线程?你的这个线程将一直存在到第一个非空闲等待操作符之前!
PPS 如果您已经编写
BackgroundService过 - 在循环之后等待所有生成的任务完成是值得的。