线程未从Thread.Sleep()唤醒

时间:2020-03-05 18:43:46  来源:igfitidea点击:

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

解决方案

回答

我们是否尝试过使用Monitor.Pulse(确保线程在运行此线程之前正在使用线程管理)来使线程执行某些操作?如果这行得通,那么我们将不得不更多地研究线程逻辑。

回答

We put the Thread.Sleep in after a couple of times when the database had gone away and we came back to 3Gb logs files full of database connection errors.

我认为一个更好的选择是使它成为日志记录系统捕获重复项,以便它可以写类似"上一条消息重复N次"的内容。

假设我已经写了一个标准的注释,说明如何在最后一个可能的时刻打开连接并尽早关闭它,而不是像这样做那样跨越一个潜在的巨大功能(但这也许是一种人工制品演示代码和应用程序实际上是正确编写的)。

当我们说它正在报告所描述的错误时,是否表示该处理程序正在报告该错误?我不清楚的原因是,在代码段中我们说"出了点问题",但我们并未在描述中说过。我不希望这是如此愚蠢,因为异常在其他地方被捕获,并且代码在睡眠以外的地方被卡住了。

回答

挖掘并找出答案?在那个混蛋上放一个调试器!

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

  • 日志系统挂起;
  • 线程退出正常,但服务仍在运行,因为其他部分发生逻辑错误。

也许(但几乎可以肯定)不是:

  • 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(),这有可能导致资源泄漏。只是要考虑的事情。

回答

我遇到了完全相同的问题。将Sleep线移到异常处理程序之外可以为我解决问题,如下所示:

bool hadError = false;
try {
  ...
} catch (...) {
  hadError = true;
}
if (hadError)
  Thread.Sleep(...);

中断线程似乎在异常处理程序的上下文中不起作用。