题
我们有一个用 C# 编写的 Windows 服务。该服务生成一个执行此操作的线程:
private void ThreadWorkerFunction()
{
while(false == _stop) // stop flag set by other thread
{
try
{
openConnection();
doStuff();
closeConnection();
}
catch (Exception ex)
{
log.Error("Something went wrong.", ex);
Thread.Sleep(TimeSpan.FromMinutes(10));
}
}
}
当数据库消失几次后,我们将 Thread.Sleep 放入,然后我们回到充满数据库连接错误的 3Gb 日志文件。
几个月来一直运行良好,但最近我们看到了一些 log.Error() 语句记录“System.InvalidOperationException:此SqlTransaction已完成;它不再可用”异常,然后再也不会回来。该服务可以运行数天,但不会再记录任何内容。
读完一些书后,我知道 Thread.Sleep 并不理想,但为什么它永远不会回来呢?
解决方案
深入挖掘并找出答案?在那个混蛋身上安装调试器!
我至少可以看到以下几种可能性:
- 日志系统挂起;
- 线程退出得很好,但服务仍在运行,因为其他部分有逻辑错误。
也许,但几乎肯定不是,以下内容:
- Sleep() 挂起。
但无论如何,附加调试器都会向您显示线程是否仍然存在以及它是否确实已挂起。
其他提示
当数据库消失几次后,我们将 Thread.Sleep 放入,然后我们回到充满数据库连接错误的 3Gb 日志文件。
我认为更好的选择是让您的日志系统捕获重复项,以便它可以编写类似“上一条消息重复了 N 次”的内容。
假设我已经写了一个标准注释,说明如何在最后可能的时刻打开连接并尽早关闭它,而不是像您所做的那样跨越潜在的巨大功能(但这也许是一个人为因素)您的演示代码和您的应用程序实际上已正确编写)。
当您说它报告您所描述的错误时,您的意思是 这个处理程序 正在报告错误?我不清楚的原因是,在代码片段中你说“出了问题”,但你在描述中没有这么说;我不希望这变得如此愚蠢,因为异常在其他地方被捕获,并且代码被卡在睡眠以外的地方。
我也遇到过同样的问题。将 Sleep 行移到异常处理程序之外解决了我的问题,如下所示:
bool hadError = false;
try {
...
} catch (...) {
hadError = true;
}
if (hadError)
Thread.Sleep(...);
中断线程似乎在异常处理程序的上下文中不起作用。
你有没有尝试过使用 监测脉冲 (在运行之前确保您的线程正在使用线程管理)让线程执行某些操作?如果这有效,那么您将不得不更多地研究您的线程逻辑。
从您发布的代码来看,尚不清楚抛出异常后系统肯定能够重新启动 - 例如如果异常来自 doStuff(),则控制流将返回(10 分钟等待后)到 openConnection(),而不会通过 closeConnection()。
但正如其他人所说,只需附加一个调试器并找到它的实际位置即可。
尝试 Thread.Sleep(10 * 60 * 1000)
我从来没有完全弄清楚发生了什么,但它似乎与 10 分钟睡眠期间抛出的 ThreadInterruptedExceptions 有关,所以我将代码更改为:
private void ThreadWorkerFunction()
{
DateTime? timeout = null;
while (!_stop)
{
try
{
if (timeout == null || timeout < DateTime.Now)
{
openDatabaseConnections();
doStuff();
closeDatabaseConnections();
}
else
{
Thread.Sleep(1000);
}
}
catch (ThreadInterruptedException tiex)
{
log.Error("The worker thread was interrupted... ignoring.", tiex);
}
catch (Exception ex)
{
log.Error("Something went wrong.", ex);
timeout = DateTime.Now + TimeSpan.FromMinutes(10);
}
}
}
除了专门捕获 ThreadInterruptedException 之外,这感觉更安全,因为所有休眠都发生在 try 块内,因此发生的任何意外情况都会被记录下来。如果我发现更多信息,我会更新这个答案。
在寻找我自己的 Thread.Sleep 问题时偶然发现了这一点。这可能相关,也可能不相关,但如果你的 doSomething() 抛出异常, closeDatabaseConnections() 将不会发生,这有可能导致资源泄漏。我会把它放在finally块中。只是需要思考一下。