一尘不染

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

linux

帮助客户解决他们遇到的问题。我更多地是sysadmin /
DBA的人,所以我在努力帮助他们。他们说这是内核/环境中的错误,在我坚持要在他们的代码中或寻求供应商对OS的支持之前,我试图证明或证明这一点。

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

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

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

  • 服务器程序启动并运行->立即接受连接
  • 服务器程序未运行,机器和网络正常->连接立即失败,错误为“连接被拒绝”
  • 机器或网络崩溃或掉线->连接需要很长时间才能失败,并显示错误“无法路由到主机”

问题在于,取消已锁定互斥锁的线程(设置了清除处理程序以解锁互斥锁)有时无法解锁互斥锁。

这样一来,主线程便无法锁定互斥锁。

详细的环境信息:

  • glibc-2.5-65
  • glibc-2.5-65
  • libcap-1.10-26
  • 内核调试2.6.18-274.el5
  • glibc-headers-2.5-65
  • glibc-common-2.5-65
  • libcap-1.10-26
  • 内核文档2.6.18-274.el5
  • 内核-2.6.18-274.el5
  • kernel-headers-2.6.18-274.el5
  • glibc-devel-2.5-65

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

任何建议和指导都将不胜感激


阅读 328

收藏
2020-06-07

共1个答案

一尘不染

正确的是,已取消线程不会解锁它们所持有的互斥锁,您需要手动安排该操作,这很棘手,因为您需要非常小心地在每个可能的取消点周围使用正确的清除处理程序。假设您pthread_cancel用于取消线程并设置清除处理程序pthread_cleanup_push以解锁互斥锁,则可以尝试以下两种替代方法,它们可能更容易正确使用,因此可能更可靠。

使用RAII解锁互斥锁
更可靠。在GNU / Linux
pthread_cancel上,使用type的特殊异常来实现__cxxabi::__forced_unwind,因此,取消线程时,将引发异常并取消堆栈堆栈。如果一个互斥锁被RAII类型锁定,那么如果堆栈被__forced_unwind异常取消,则可以保证其析构函数运行。
Boost Thread提供了一个可移植的C
++库,该库包装了Pthreads,并且使用起来更加容易。它提供了RAII类型boost::mutex和其他有用的抽象。Boost
Thread还提供了自己的“线程中断”机制,该机制类似于Pthread取消,但不相同,并且提供了Pthread取消点(例如connect)不是Boost
Thread中断点,这对某些应用程序可能会有帮助。但是,对于您的客户而言,由于取消的目的是中断connect调用,因此他们可能确实希望坚持使用Pthread取消。GNU
/ Linux(非便携式)实现取消的方式是一种例外,这意味着它将与配合使用boost::mutex

在用C 编写时,确实没有任何借口显式锁定和解锁互斥锁。恕我直言,C 最重要和最有用的 功能是析构函数,它是自动释放互斥锁等资源的理想选择。

另一种选择是使用一个强大的互斥体,它是通过调用创建pthread_mutexattr_setrobust一个pthread_mutexattr_t初始化互斥之前。如果线程在持有健壮的互斥锁时死亡,内核将对其进行记录,以便下一个试图锁定互斥锁的线程获得特殊的错误代码EOWNERDEAD。如果可能,新线程可以使该线程保护的数据再次保持一致,并获得互斥锁的所有权。与仅使用RAII类型锁定和解锁互斥锁相比,要正确使用它要困难得多。

完全不同的方法是确定在调用时是否真的需要按住互斥锁connect。在慢速操作期间保持互斥不是一个好主意。connect如果成功锁定了互斥锁并更新了互斥锁正在保护的任何共享数据,那么您不能打电话吗?

我的首选是同时使用Boost Thread和避免长时间保持互斥。

2020-06-07