首页 文章

取消已锁定互斥锁的线程不会解锁互斥锁

提问于
浏览
8

帮助客户解决他们遇到的问题 . 我更像是一个系统管理员/ DBA人,所以我正在努力帮助他们 . 他们说这是内核/环境中的一个错误,我试图证明或反驳它之前我坚持认为它是在他们的代码中或寻求供应商支持操作系统 .

发生在Red Hat和Oracle Enterprise Linux 5.7(和5.8)上,应用程序是用C语言编写的

他们遇到的问题是主线程启动一个单独的线程来执行可能长时间运行的TCP connect()[客户端连接到服务器] . 如果“长时间运行”方面花费的时间太长,它们会取消线程并启动另一个线程 .

这样做是因为我们不知道服务器程序的状态:

  • 服务器程序启动并运行 - >立即接受连接

  • 服务器程序未运行,机器和网络正常 - >连接立即失败,错误'connection refused'

  • 机器或网络崩溃或关闭 - >连接需要很长时间才能失败并出现错误'no route to host'

问题是取消锁定互斥锁的线程(清理处理程序设置为解锁互斥锁)有时不解锁互斥锁 .

这使得主线程试图锁定互斥锁 .

详细环境信息:

  • glibc-2.5-65

  • glibc-2.5-65

  • libcap-1.10-26

  • kernel-debug-2.6.18-274.el5

  • glibc-headers-2.5-65

  • glibc-common-2.5-65

  • libcap-1.10-26

  • kernel-doc-2.6.18-274.el5

  • kernel-2.6.18-274.el5

  • kernel-headers-2.6.18-274.el5

  • glibc-devel-2.5-65

代码是使用以下代码构建的:c -g3 tst2.C -lpthread -o tst2

非常感谢任何建议和指导

2 回答

  • 4

    它's correct that cancelled threads do not unlock mutexes they hold, you need to arrange for that to happen manually, which can be tricky as you need to be very careful to use the right cleanup handlers around every possible cancellation point. Assuming you'使用 pthread_cancel 来取消线程并使用 pthread_cleanup_push 设置清理处理程序来解锁互斥锁,你可以尝试一些替代方案,这可能更容易实现,因此可能更可靠 .

    使用RAII解锁互斥锁会更可靠 . 在GNU / Linux上 pthread_cancel 是使用 __cxxabi::__forced_unwind 类型的特殊异常实现的,因此当一个线程被取消时,抛出异常并解除堆栈 . 如果互斥锁被RAII类型锁定,那么如果堆栈被 __forced_unwind 异常展开,则它的析构函数将保证运行 . Boost Thread提供了一个可移植的C库,它包装Pthreads并且更容易使用 . 它提供了RAII类型 boost::mutex 和其他有用的抽象 . Boost Thread还提供了自己的"thread interruption"机制,类似于Pthread取消但不相同,而Pthread取消点(例如 connect )不是Boost Thread中断点,这对某些应用程序很有帮助 . 但是在你的客户端的情况下,由于取消点是打断 connect 调用,他们可能确实想要坚持使用Pthread取消 . (非可移植)方式GNU / Linux实现取消作为异常意味着它将与 boost::mutex 一起使用 .

    当你用C语言编写时,没有理由显式锁定和解锁互斥锁,恕我直言C的 most important and most useful 功能是析构函数,它是自动释放互斥锁等资源的理想选择 .

    另一种选择是使用强健的互斥锁,它是在初始化互斥锁之前通过在 pthread_mutexattr_t 上调用pthread_mutexattr_setrobust而创建的 . 如果一个线程在持有一个健壮的互斥锁时死掉,内核会记下它,以便试图锁定互斥锁的下一个线程获得特殊的错误代码 EOWNERDEAD . 如果可能,新线程可以使线程保护的数据再次保持一致并获得互斥锁的所有权 . 这比使用RAII类型锁定和解锁互斥锁要困难得多 .

    一种完全不同的方法是在调用 connect 时确定是否确实需要保持互斥锁 . 在慢速操作期间持有互斥锁并不是一个好主意 . 你能不能调用 connect 然后成功锁定互斥锁并更新互斥锁保护的共享数据?

    我倾向于使用Boost Thread并避免长时间持有互斥锁 .

  • 14

    他们遇到的问题是主线程启动一个单独的线程来执行可能长时间运行的TCP connect()[客户端连接到服务器] . 如果“长时间运行”方面花费的时间太长,它们会取消线程并启动另一个线程 .

    琐碎的修复 - 不要取消线程 . 它有害吗?如有必要,请检查线程(当 connect 最终完成时)是否仍需要连接,如果不是,则关闭它,释放互斥锁,然后终止 . 您可以使用受互斥锁保护的布尔变量来执行此操作 .

    此外,线程在等待网络I / O时不应持有互斥锁 . 互斥锁应仅用于快速且主要受CPU限制或可能受本地磁盘限制的内容 .

    最后,如果您觉得需要从外部进入并迫使线程做某事,请退后一步 . 您编写了该线程的代码 . 如果你觉得有需要,那就意味着你不必从外面"push it around" .

相关问题