我正在与HttpListener
作斗争,并等待它的上下文。有时我真的很困惑。我有运行此服务器的应用程序。然而,我不想等待这些请求。我想在后台运行服务器,并不关心它。但我不确定这是否是个好主意,因为微软说,如果你有类似I/O操作的东西,应该等待它。但我不想等待。如果我想在控制台上写点什么呢?我不能,因为我在等待HttpListener的上下文。解决方案看起来很简单:
// _server is an instance of HttpListener
public void StartServer()
{
_server.Prefixes.Add("http://127.0.0.1:8080/test/");
_server.Start();
Task.Run(async() =>
{
while (_server.IsListening)
{
HttpListenerContext context = await _server.GetContextAsync();
await HandleContextAsync(context);
}
}).ConfigureAwait(false);
}
然而,在这种情况下,我不确定是否会出现任何死锁或其他情况,因为处理上下文需要大量操作(例如,它在某个地方打开一个DB连接)。
我决定做的最后一件事是将我的主应用程序和服务器端分开,但我仍然希望在后台运行服务器,以便能够使用控制台的一些输入来停止它。我该怎么办?
我也试过这样的东西:
Task serverListener = server.StartServerAsync();
Console.ReadLine();
await server.StopServerAsync();
//
// The server methods:
//
public async Task StartServerAsync()
{
_server.Prefixes.Add("http://127.0.0.1:8080/test/");
_server.Start();
while (_server.IsListening)
{
HttpListenerContext context = await _server.GetContextAsync();
await HandleContextAsync(context);
}
}
public Task StopServerAsync()
{
_server.Stop();
_server.Close();
return Task.CompletedTask;
}
但在这种情况下,我不确定Task会发生什么,因为它没有被取消或其他什么。服务器已停止,但任务仍然不可用。它是否比Task.Run()
的第一种情况更好?
首先,我建议使用像Kestrel这样已经构建好的解决方案。如果您构建自己的解决方案,您将需要做出一些决定,并自己编写大量代码。
微软表示,如果您有类似I/O操作的操作,应该等待。但我不想等它。
这是一般规则,是的。有两个原因:
- 通过
await
ing,您的应用程序知道操作何时完成。如果你的应用程序不知道何时完成,那么它就不知道何时可以安全退出 - 通过
await
ing,您的应用程序可以检测并响应所有异常
我不确定不会有任何死锁或
死锁在这里不是问题。假设是Console应用程序,那么默认情况下,所有的延续都将在线程池上运行。注意:ConfigureAwait(false)
在您发布的代码中没有做任何事情;ConfigureAwait
配置await
,但没有await
。在控制台应用程序的情况下,ConfigureAwait(false)
与根本没有它是一样的。
我仍然希望在后台运行服务器,以便能够使用控制台的一些输入来停止它。
您可以使用Task.Run
启动它,并将结果保存在Task
变量中,然后在应用程序结束时保存该变量await
。或者如果你想要一个";顶层循环";任务,然后让代码在中。Task.Run
使用顶级try
/catch
并以这种方式报告错误。
你还需要考虑你希望你的关机有多干净。大多数web服务器在被请求退出时不会立即退出;它们允许任何未完成的请求完成(并关闭它们的侦听套接字,这样就不会有新的请求进入)。如果您希望确保干净的关机,您可能希望await
该任务。
此外,您当前的代码有一个";顶层循环";任务,但在侦听下一个连接之前,它会处理每个连接。对于web或TCP/IP服务器来说,这是非常不寻常的行为;对于每个连接来说,启动其自己的"是正常的;顶层循环";以处理每个请求,从而不会阻止其他传入请求。如果你想要一个干净的关闭,这自然会使你需要编写的关闭逻辑复杂化(你现在必须管理一个监听器和一个连接集合)。
或者你可以使用Kestrel,它为你做这一切。:)
如果你真的想自己做,你可能会发现我正在入侵的一个新图书馆很有趣。它是专门设计来帮助处理这些";顶级循环";以更结构化的方式。不过,目前它处于一个非常早期的预发布alpha状态。
然而,在这种情况下,我不确定是否会出现任何死锁或
不,这不会引入任何死锁(除了已经存在的死锁(如果有的话))。CCD_ 17在线程池上调度代码的执行;流动;CCD_ 18,这是几种类型的异步相关死锁的臭名昭著的原因。
我也尝试过这样的东西:但在这种情况下,我不确定Task会发生什么,因为它没有被取消或其他什么。
操作系统将处理关闭时应用程序所请求的所有资源的清理。由于侦听器被停止,StartServerAsync
任务本身将被设置为具有异常结果,但由于没有等待,因此不会观察到异常(尽管我认为在这种情况下这是可以的),仅此而已
阅读更多:
- 斯蒂芬·克利里的《不要阻塞异步代码》
- ExecutionContext与SynchronizationContext——如果你真的想深入了解的话