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

dwa*_*oov 10 c c++ linux mutex

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

发生在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的报头-2.5-65
  • 的glibc-共2.5-65
  • 的libcap-1.10-26
  • 内核DOC-2.6.18-274.el5
  • 内核2.6.18-274.el5
  • 内核头文件,2.6.18-274.el5
  • 的glibc-devel的-2.5-65

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

非常感谢任何建议和指导

Jon*_*ely 17

取消的线程没有解锁它们所持有的互斥锁是正确的,你需要安排手动进行,这可能很棘手,因为你需要非常小心地在每个可能的取消点周围使用正确的清理处理程序.假设您正在使用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还提供了自己的"线程中断"机制,类似于Pthread取消但不相同,而Pthread取消点(例如connect)不是Boost Thread中断点,这对某些应用程序很有帮助.但是在您的客户端的情况下,因为取消点是中断connect呼叫,他们可能希望坚持使用Pthread取消.(非可移植)方式GNU/Linux实现取消作为例外意味着它可以很好地使用boost::mutex.

当您使用C++编写时,没有理由明确锁定和解锁互斥锁,恕我直言,C++ 最重要和最有用的功能是析构函数,它是自动释放互斥锁等资源的理想选择.

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

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

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

  • 是的,RAII将更可靠地解锁.这是否可取是另一回事; 锁可以保护对可能处于不一致状态的对象的访问,并且只有在已知受保护对象处于一致状态时才能解锁该锁.在异常过程中自动解锁锁定是正确的做法,这是不言而喻的. (4认同)