取消锁定互斥锁的线程不会解锁互斥锁
帮助客户解决他们遇到的问题。 我更像是一个系统管理员/ DBA家伙,所以我正在努力帮助他们。 他们说这是内核/环境中的一个错误,我试图在我坚持认为它在代码中或寻求供应商支持操作系统之前证明或反驳。
在Red Hat和Oracle Enterprise Linux 5.7(和5.8)上发生,应用程序是用C ++编写的
他们遇到的问题是主线程启动一个单独的线程来执行潜在的长时间运行的TCP连接()[client to client]。 如果'长时间运行'方面花费太长时间,他们会取消线程并启动另一个线程。
这是因为我们不知道服务器程序的状态:
问题是取消锁定互斥锁的线程(使用清理处理程序设置为解锁互斥锁)有时不会解锁互斥锁。
这就让主线程试图锁定互斥锁。
详细的环境信息:
代码是用以下代码构建的:c ++ -g3 tst2.C -lpthread -o tst2
任何意见和指导,非常感谢
被取消的线程无法解锁它们所持有的互斥锁是正确的,您需要安排它们手动执行,这可能非常棘手,因为您需要非常小心地在每个可能的取消点周围使用正确的清理处理程序。 假设你正在使用pthread_cancel
取消线程和设定清除处理程序与pthread_cleanup_push
解锁互斥,有几个选择,你可以尝试,这可能是简单的得到的权利,因此可能会更可靠。
使用RAII解锁互斥锁将更加可靠。 在GNU / Linux上, pthread_cancel
是通过类型__cxxabi::__forced_unwind
一个特殊的例外来__cxxabi::__forced_unwind
,所以当一个线程被取消时抛出异常并且栈被解开。 如果一个互斥锁被RAII类型锁定,那么它的析构函数将保证在堆栈被__forced_unwind
异常解除时运行。 Boost Thread
提供了一个包装Pthreads的便携式C ++库,使用起来更加简单。 它提供了RAII类型的boost::mutex
和其他有用的抽象。 Boost Thread也提供了自己的“线程中断”机制,类似于Pthread取消但不相同,并且Pthread取消点(如connect
)不是Boost线程中断点,这对于某些应用程序可能会有所帮助。 然而,在客户的情况下,因为取消点是中断connect
呼叫,他们可能会想要坚持Pthread取消。 GNU / Linux的(非可移植)方式将取消作为异常意味着它可以很好地与boost::mutex
。
当你用C ++编写时,真的没有理由显式锁定和解锁互斥,恕我直言,C ++的最重要和最有用的特性是析构函数,它非常适用于自动释放资源(如互斥锁)。
另一种选择是使用一个强大的互斥体,它是通过调用创建pthread_mutexattr_setrobust
上pthread_mutexattr_t
初始化互斥之前。 如果一个线程在持有可靠的互斥锁时死亡,内核将记下它,以便尝试锁定互斥锁的下一个线程获取特殊的错误代码EOWNERDEAD
。 如果可能,新线程可以使线程保护的数据再次保持一致,并获得互斥锁的所有权。 这比使用RAII类型来锁定和解锁互斥锁要困难得多。
一个完全不同的方法是确定在调用connect
时是否确实需要保持互斥锁。 在缓慢操作期间持有互斥体不是一个好主意。 你不能调用connect
那么如果成功锁定互斥锁并更新互斥体正在保护的共享数据?
我的首选是使用Boost Thread并避免长时间持有互斥锁。
他们遇到的问题是主线程启动一个单独的线程来执行潜在的长时间运行的TCP连接()[client to client]。 如果'长时间运行'方面花费太长时间,他们会取消线程并启动另一个线程。
微不足道的修复 - 不要取消线程。 它有害吗? 如有必要,进行线程检查(当connect
最终完成时)是否仍然需要连接,如果没有,则关闭它,释放互斥锁并终止。 你可以用一个由互斥体保护的布尔变量来做到这一点。
另外,线程在等待网络I / O时不应该保持互斥。 互斥锁只能用于速度较快且主要受CPU限制或可能受本地磁盘限制的情况。
最后,如果你觉得你需要从外面伸手去强迫一个线程去做某件事,那就退后一步。 你为该线程编写了代码。 如果你觉得需要,那就意味着你没有编写该线程去做你真正想做的事情。 修正是修改线程去做什么,只做什么,你真正想要的。 那么你不必从外面“推”它。
链接地址: http://www.djcxy.com/p/92157.html上一篇: Cancelling a thread that has a mutex locked does not unlock the mutex