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

帮助客户解决他们遇到的问题.我更像是一个系统管理员/ 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
> 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

非常感谢任何建议和指导

最佳答案
取消的线程没有解锁它们所持有的互斥锁是正确的,你需要安排手动进行,这可能很棘手,因为你需要非常小心地在每个可能的取消点周围使用正确的清理处理程序.假设您正在使用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中断点,这对某些应用程序很有帮助.但是在客户端的情况下,由于取消点是中断连接调用,他们可能希望坚持使用Pthread取消. (非可移植)方式GNU / Linux实现取消作为例外意味着它将与boost :: mutex一起使用.

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

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

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

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

转载注明原文:c – 取消已锁定互斥锁的线程不会解锁互斥锁 - 代码日志