之前我注意到了这种行为,这次我想问一个问题:
我有一个简单的“概念证明”程序,它会产生一些线程,等待它们做一些工作,然后退出。
但是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
根据第10.2条,ECMA C#语言规范的“申请终止”:
如果入口点方法的返回类型是
void
,到达终止该方法的右大括号(}
),或执行没有表达式的return
语句,结果终止状态代码为0。
调试器确认正在到达右括号,但标准没有明确说明离开Main
将退出应用程序,只是设置了终止状态代码。
它还提到:
...调用所有尚未被垃圾回收的[应用程序]对象的终结器,除非已经抑制了这种清理(例如,通过调用库方法
GC.SuppressFinalize
)。 / p>
我怀疑幕后终结器可能是问题所在,因为服务器对象实现了IDisposable
,并且有一个调用Dispose
的终结器并不罕见。但是当程序被终止时,CLR将终结器限制为执行两秒钟(以防万一在我尝试在服务器对象上调用GC.SuppressFinalize
的超时时发生了奇怪的事情,并得到了相同的结果)。
我对服务器线程可以做什么来无限期地终止应用程序感到有点难过。
答案 0 :(得分:0)
@CarstenKönig链接中使用的措辞让我意识到我在查找错误的文档。问题确实是启动服务器实现的线程是前台线程,将其更改为后台线程会导致ThreadPool实现按预期运行。
似乎ECMA标准推迟了特定的终止行为(CLI文档也未提及任何相关内容)。我还在寻找能否找到更详细地描述整个终止程序的一般文件。