该程序有一个后台任务(通过 HttpListener 等待请求),在这个任务内部,当请求到达时,我创建一个任务来处理请求上下文本身。我把处理请求上下文的任务放到了一个HashSet中,请求可以被处理很长时间(最长5秒),并且任务会周期性的累积在HashSet中。我想运行一个后台任务来控制HashSet,即 获取第一个完成的任务,将其从 HashSet 中移除并记录任务完成的结果
StartListen()
- 启动所有任务,但启动时没有收到任何请求且HashSet为空,因此无法通过WhenAny等待第一个完成的任务。我可以手动循环,例如,每 100 毫秒一次,检查 HashSet 的元素并在那里查找已完成的任务。所以我有一个问题,这是手动完成的还是可以通过其他方式完成的?
private readonly HashSet<Task<Result>> _httpContextTasks = new HashSet<Task<Result>>();
public Result<Task> StartListen()
{
_cts = new CancellationTokenSource();
_bgTask = BackgroundController4ContextHandlers(_cts.Token);
return ListenHttpAsync(_cts.Token);
}
private async Task BackgroundController4ContextHandlers(CancellationToken ct)
{
await Task.Run(async () =>
{
while (!ct.IsCancellationRequested)
{
var completedTask = await Task.WhenAny(_httpContextTasks);
_httpContextTasks.Remove(completedTask);
var res = completedTask.Result;
var strResult = res.ToString();
_logger.Information("{HttpServer}","ЗАПРОС ОБРАБОТАН", strResult);
}
_logger.Information("{HttpServer}","ФОНОВАЯ обработка Task запросов остановлена");
}, ct);
}
private async Task ListenHttpAsync(CancellationToken ct)
{
_listener.Start();
_logger.Information("{HttpServer}", "Ожидание запросов ...");
while (!ct.IsCancellationRequested)
{
try
{
var context = await _listener.GetContextAsync();
var handler = HttpListenerContextHandlerAsync(context, ct);
_httpContextTasks.Add(handler);
}
catch (TaskCanceledException) { }
}
}
如果我只是正面优化代码,那么我会有这样的东西
唯一需要注意的是,我根本不希望这里有正在运行的任务,而且我不会将它存储在任何地方。因此,我在continuation内部处理了一个异常,但是也需要考虑到continuation本身内部的所有异常也都被处理了,并且成功完成了。这种方法在性能方面更酷,没有你必须不断跑来跑去寻找已完成任务的集合。该任务在成功或失败时简单地触发一个回调。