帮助客户解决他们遇到的问题。我更像是一个系统管理员/DBA的人,所以我很难帮助他们。他们说这是内核/环境中的一个bug,在我坚持在他们的代码中或寻求供应商对操作系统的支持之前,我试图证明或否认这一点。
发生在Red和OracleEnterpriseLinux5.7(和5.8)上,应用程序是用C++编写的
他们遇到的问题是,主线程启动一个单独的线程来执行一个可能长期运行的connecting ()客户机连接到服务器。如果“长时间运行”方面花费的时间太长,则会取消线程并启动另一个线程。
这是因为我们不知道服务器程序的状态:
问题是,取消已锁定互斥锁的线程(设置清理处理程序以解除互斥锁)有时并不会解除互斥锁。
这使得主线程不得不试图锁定互斥对象。
详细环境信息:
代码是用: c++ -g3 tst2.C -lpthread -o tst2构建的
我们非常感谢您的任何建议和指导。
发布于 2013-01-10 22:45:32
正确的是,取消的线程不解锁它们所持有的互斥项,需要手动安排,这可能很棘手,因为您需要非常小心地在每个可能的取消点周围使用正确的清理处理程序。假设您使用pthread_cancel取消线程,并使用pthread_cleanup_push设置清理处理程序以解除互斥锁,那么您可以尝试几种更简单、更可靠的替代方法。
使用雷伊解锁互斥锁将更加可靠。在GNU上,pthread_cancel是用__cxxabi::__forced_unwind类型的特殊异常实现的,因此当取消线程时,会抛出异常并解除堆栈。如果互斥锁被RAII类型锁定,那么如果堆栈被__forced_unwind异常解压缩,那么它的析构函数将被保证运行。Boost Thread提供了一个可移植的C++库,它封装了线程,而且使用起来容易得多。它提供了RAII类型的boost::mutex和其他有用的抽象。Boost Thread还提供了自己的“线程中断”机制,这种机制类似于线程取消,但并不相同,而P线程取消点(如connect)不是Boost线程中断点,这对于某些应用程序是有帮助的。但是,在客户端的情况下,由于取消的目的是中断connect调用,所以他们很可能会坚持使用P线程取消。GNU/Linux实现取消的(不可移植的)方式是一个例外,这意味着它将在boost::mutex中很好地工作。
在用C++编写时,没有任何理由可以显式地锁定和解锁互斥锁,IMHO ---- C++最重要和最有用的特性是析构函数,它们是自动释放资源(如互斥锁)的理想方法。
另一种选择是使用一个健壮的互斥体,它是在初始化互斥对象之前通过在一个pthread_mutexattr_setrobust上调用pthread_mutexattr_t创建的。如果一个线程在保存一个健壮的互斥锁时死了,内核将记下它,以便下一个试图锁定互斥锁的线程获得特殊的错误代码EOWNERDEAD。如果可能,新线程可以使受线程保护的数据再次保持一致,并获得互斥锁的所有权。这比简单地使用RAII类型锁定和解锁互斥锁要难得多。
另一种完全不同的方法是在调用connect时决定是否真的需要保持互斥锁。在缓慢的操作中保持互斥不是一个好主意。那么,如果成功锁定互斥对象并更新由互斥体保护的共享数据,您不能调用connect吗?
我更喜欢使用Boost Thread和避免长时间保持互斥对象。
发布于 2013-01-11 01:24:54
他们遇到的问题是,主线程启动一个单独的线程来执行一个可能长期运行的connecting ()客户机连接到服务器。如果“长时间运行”方面花费的时间太长,则会取消线程并启动另一个线程。
琐碎的修复--不要取消线程。这有什么害处吗?如果有必要,让线程检查(当connect最终完成时)是否仍然需要连接,如果不需要,则关闭它,释放互斥,然后终止。您可以使用由互斥体保护的布尔变量来完成此操作。
另外,在等待网络I/O时,线程不应该持有互斥对象。Mutexes应该只用于那些速度快且主要是CPU的东西--限制或可能受到本地磁盘的限制。
最后,如果你觉得你需要从外面伸出手来,强迫一根线做些什么,那就退后一步。你为那个线程写了代码。如果您觉得需要,这意味着您没有编码该线程来执行您真正希望它做的事情。修复方法是修改线程来执行实际需要的事情,并且只做什么。这样你就不用从外面“推”它了。
https://stackoverflow.com/questions/14268080
复制相似问题