【问题标题】:Debugging a Multi-Threaded server调试多线程服务器
【发布时间】:2014-10-07 13:35:42
【问题描述】:

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

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

"Some Server Related Message..."

代码如下:

CalledFunc ()
{
    Code ...

    Acquiring Thread lock
    Line printing "Some Server Related Message..."
    Func();
    Releasing Thread Lock
}
  1. 负责调试的程序员应该怎么做?
  2. Func() 出了什么问题?
  3. 如果Func() 中抛出异常,应该如何解决问题?

【问题讨论】:

  • 是什么样的应用程序?
  • 你忘了问问题。你给我们讲了一个很棒的故事,大概你有一些问题,但你忘了问那个问题。像大多数面试问题一样,没有“一个正确的答案”。面试官想听听你如何推理线程应用程序以及你如何调试它们。此外,调试伪代码非常困难。我们在谈论什么样的锁(递归?错误检查?)以及我们如何获取它(锁功能?RAII?)很重要。另外,为什么这被标记为C?这不是 C++ 代码还是我们在谈论一些 C 异常方案?如果有,是什么?
  • @java_geek:正如我们所说,我是多线程和学习的新手。面试官没有具体说明这是什么类型的应用程序,只是为了找出代码和样式的问题。
  • @joeroot:这个问题是主观的;因此,提出正确的问题会给面试官留下良好的印象。通常对于此类问题,您需要大声思考,以便面试官了解您的思考过程。只是一个建议:)

标签: c++ multithreading thread-safety pthreads


【解决方案1】:

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

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

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

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

【讨论】:

  • 我假设您正在处理一个 Web 应用程序。有关应用程序性质的更多详细信息会有所帮助
  • 因为这显然是 c++ 我会提到使用 RAII 进行锁/互斥/事件等
【解决方案2】:

很可能是:

  • Func() 正在尝试再次获取锁(易于检查),或者
  • Func() 抛出了锁被锁定的异常(更可能和微妙)

所以:

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

【讨论】:

    【解决方案3】:

    要克服 Func() 中的异常问题,您可以使用范围锁。 RAII 是确保异常安全和避免一般泄漏的好方法。该链接也恰好有一个互斥锁作为示例。

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

    【讨论】:

      【解决方案4】:

      我认为他们在哪里寻找这个:

      负责调试的程序员应该怎么做?

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

      Func() 出了什么问题?

      根据下一个问题,我们可以假设它一定是抛出了一个异常,导致锁永远不会被释放,或者它试图再次获得锁导致死锁。

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

      使用 RAII 来保证异常安全和更好/更简洁的代码。

      【讨论】:

      • 您似乎认为这是一个特定于 Windows 的问题,即使它被标记为“pthreads”。
      • 可能是因为 pthreads 是跨平台的,所以为 Linux 调试器切换 "windbg" ;)
      猜你喜欢
      • 2020-03-21
      • 1970-01-01
      • 1970-01-01
      • 2017-03-24
      • 1970-01-01
      • 1970-01-01
      • 2014-04-24
      • 2015-08-11
      相关资源
      最近更新 更多