我有一个控制台应用程序,它(除其他外)使用 HttpSelfHostServer 托管一个 Web api 控制器。应用程序启动一个前台线程做一些事情,并等待用户按键,然后应用程序将退出。这很好用。我使用 Console.ReadKey 等待输入。
当我添加一个 HttpSelfHostServer 和一个 Web Api 控制器时,会发生奇怪的事情。每次在控制器中接收到一个 http 请求时,都会使用一个新线程。我猜它会从 Threadpool 中获取一个线程。在这个线程中,我做了一个 Console.WriteLine()。这在大多数情况下都有效,直到突然停止工作。处理在 Console.WriteLine 中被阻止。是不是碰巧得到了在 Console.ReadKey 上等待的线程?
总而言之,我的应用程序做了三件事:
- 一个线程正在等待用户按键然后停止应用程序
- 一个线程正在做其他处理工作。
- Web Api 控制器为每个 Web 请求启动一个新线程,进行一些处理,包括一个 console.writeline。此处理相当快,并且返回 HttpStatusCode.OK 很快。除非 console.writeline 挂起。然后它永远不会返回,并且任何更多的 Web Api 调用块也在这里。直到按下一个键。然后突然所有这些阻塞的线程都被释放了。
怎么了?怎么解决?
请参阅下面的基本实现。
public static class Program
{
public WebServer MyWebServer { get; set; }
static void Main(string[] args)
{
if (Environment.UserInteractive)
{
// running as console app
Console.WriteLine($"START PROGRAM");
Start(args); // Call stuff that sets up a foreground thread to do some processing.
Console.WriteLine("Press any key to stop...");
Console.ReadKey(true);
Console.WriteLine($"STOP PROGRAM");
Stop(); // Call stuff to stop the processing in the other thread.
}
else
{
// running as service
using (var service = new Service())
{
ServiceBase.Run(service);
}
}
}
public void Start()
{
MyWebServer = new WebServer();
MyWebServer.Start();
Do_some_other_stuff_on_another_thread();
}
public void Stop()
{
Stop_the_other_thread();
MyWebServer.Stop();
}
}
public class WebServer
{
// This is a self hosting WepApi webserver.
private HttpSelfHostServer Server { get; set; }
public void Start()
{
try
{
config.Routes.MapHttpRoute("My Api", "api/{controller}/{action}");
Server = new HttpSelfHostServer(config);
Server.OpenAsync();
Console.WriteLine("STARTING WEBSERVER");
}
catch (Exception ex)
{
Console.WriteLine("WEBSERVER EXCEPTION: " + ex.Message);
}
}
public void Stop()
{
Server.CloseAsync();
Console.WriteLine("WEBSERVER STOP");
}
}
public class MyController : ApiController
{
[HttpPost]
public HttpResponseMessage Trigger()
{
try
{
// This console writeline is sometimes blocked.
Console.WriteLine("MyController.Trigger: We got a web api request");
ProcessTriggerRequest();
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError);
}
}
}