为什么Main没有返回

本文关键字:返回 Main 为什么 | 更新日期: 2023-09-27 18:18:12

我以前注意过这种行为,这次我想问一个问题:

我有一个简单的"概念验证"程序,它生成几个线程,等待它们做一些工作,然后退出。

但是Main不返回,除非我调用server.Close()(关闭套接字并结束服务器线程的执行):

private void Run()
{
    var server = StartServer(); //Starts a thread in charge of listening on a socket
    _serverResetEvent.WaitOne();
    ThriftProtocolAccessManager protocolManager = CreateProtocolManager(); //Doesn't create any threads
    const int numTestThreads = 10;
    Barrier testCompletedBarrier;
    Thread[] testThreads = GenerateTestThreads(numTestThreads, protocolManager, out testCompletedBarrier); //Creates x threads, where x is numTestThreads
    testThreads
        .AsParallel()
        .ForAll(thread => thread.Start()); //Start them "at the same time" (For testing purposes 
    testCompletedBarrier.SignalAndWait(); //Barrier has participants equal to numTestThreads + 1 and each thread calls this
    //server.Close() would go here. When it is here, the program returns as expected
    Console.WriteLine("All Threads Complete"); //This is getting called
}
private static void Main(string[] args)
{
    new Program().Run();
    Console.WriteLine("Run completed"); //This is also called
}//The debugger confirms this brace is reached as well

根据ECMA c#语言规范第10.2条"应用终止":

如果入口点方法的返回类型是void,到达右括号(})终止该方法,或者执行没有表达式的return语句,将导致终止状态码为0。

调试器确认到达右大括号,但是标准没有明确地说离开Main将退出应用程序,只是设置了终止状态码。

它还提到:

…[应用程序]中所有尚未被垃圾收集的对象的终结器将被调用,除非这种清理已经被抑制(例如,通过调用库方法GC.SuppressFinalize)。

我怀疑在幕后终结器可能是问题所在,因为服务器对象实现了IDisposable,并且有一个调用Dispose的终结器并不罕见。但是CLR限制终结器在程序终止时的执行时间为两秒(只是以防超时发生了一些奇怪的事情,我尝试在服务器对象上调用GC.SuppressFinalize并得到相同的结果)。

我有点困惑,服务器线程可能在做什么来无限期地阻止应用程序的终止

为什么Main没有返回

@Carsten König链接中使用的措辞使我意识到我在错误的文档中查找。问题确实是启动服务器实现的线程是前台线程,将其更改为后台线程会导致ThreadPool实现按照预期的方式运行。

似乎ECMA标准推迟了特定的终止行为(CLI文档也没有提到任何关于它的内容)。我还在寻找一份更详细地描述整个终止程序的通用文件。