我们有一个用 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 并不理想,但为什么它永远不会回来呢?

有帮助吗?

解决方案

深入挖掘并找出答案?在那个混蛋身上安装调试器!

我至少可以看到以下几种可能性:

  1. 日志系统挂起;
  2. 线程退出得很好,但服务仍在运行,因为其他部分有逻辑错误。

也许,但几乎肯定不是,以下内容:

  • 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块中。只是需要思考一下。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top