调试多线程服务器



我在一次面试中被问到这个问题,现在我很好奇,因为我认为面试官对我的回答不满意。问题是:

多线程服务器应用程序停止工作,来自该应用程序的最后一条日志消息为:

"Some Server Related Message..."

代码看起来像:

CalledFunc ()
{
    Code ...
    Acquiring Thread lock
    Line printing "Some Server Related Message..."
    Func();
    Releasing Thread Lock
}
  1. 负责的程序员应该做些什么来调试这个
  2. Func()发生了什么错误
  3. 如果在Func()中抛出异常,应该采取什么措施来解决问题

原因#1:这是一个数据库问题这听起来可能很奇怪,但应用程序服务器挂起的主要原因与应用程序服务器本身没有直接关系。症状的位置很少是根本原因的位置。以下情况非常常见:

数据库出现瓶颈,导致查询运行速度比平时慢。过去需要1秒的请求,现在需要5秒才能完成。并发请求的平均数量缓慢增加(由于积压)。服务器的线程用完,应用程序服务器挂起。如果您设法获得一个线程转储,您只会看到一堆线程在等待,而另一个组正在实际运行。另一种可能性是,等待线程(或排队线程)的数量将吞噬所有可用内存,最终导致OutOfMemory错误。

原因#2:死锁如果应用程序服务器似乎什么都没做,请查找死锁。这些可能是导致SQL查询挂起或查找更新语句的数据库死锁。例如,如果日志表被锁定,则为每个请求写入数据库的事务日志可能很容易挂起整个应用程序。还要检查共享对象——一个同时从多个线程写入的操作系统文件。

原因#3:线程失控在应用程序服务器确实是罪魁祸首的情况下,您应该寻找一个失控的线程。这些很难检测,因为它们几乎不会出现在日志中,因为它们通常只在请求完成时写入。在已经影响到整个应用程序之前,运行线程可能不会返回。因此,挂起请求将不会写入日志。这些"失控"线程通常包括无限循环或代码,这些循环或代码会消耗过多堆内存,从而导致内存不足。例如,一个应该显示不包括在结果页面之间分页选项的结果的查询突然需要显示大量结果。该页面需要很长时间才能呈现并破坏应用程序服务器,最终导致其挂起。

很可能是:

  • Func()正在尝试再次获取锁(易于检查),或者
  • Func()在锁被锁定的情况下引发了异常(更可能更微妙)

因此:

  1. 检查Func()的代码,以检查是否所有可能的路径(包括异常)都释放了锁
  2. 以上两个选项之一
  3. 在抛出异常之前释放锁,或者在CalledFunc()中捕获异常并释放锁

要解决Func()中异常的问题,可以使用作用域锁。RAII是确保异常安全和一般情况下避免泄漏的好方法。这个链接也恰好有一个互斥对象作为例子。

此外,在日志中看到这一行并不意味着问题来自代码的这一部分。

我认为他们在寻找这个:

负责的程序员应该做些什么来调试这个?

获取进程的挂起转储,然后使用windbg找出原因,即如果它是死锁,那么从转储中可以明显看出。

Func()中发生了什么错误?

根据下一个问题,我们可以假设它一定在某个时候抛出了异常,导致锁永远无法释放,或者它试图再次获取锁,导致死锁。

如果在Func()中抛出异常,应该做些什么来修复问题

使用RAII以确保异常安全,并获得更好/更干净的代码。

最新更新