为啥在 pthread_detach() 之后调用 pthread_exit() 在极少数情况下会导致 SEGV?
Posted
技术标签:
【中文标题】为啥在 pthread_detach() 之后调用 pthread_exit() 在极少数情况下会导致 SEGV?【英文标题】:Why does pthread_exit() in rare cases cause a SEGV when called after pthread_detach()?为什么在 pthread_detach() 之后调用 pthread_exit() 在极少数情况下会导致 SEGV? 【发布时间】:2012-07-11 15:47:53 【问题描述】:在我对pthread_join()
的调用中,当我的应用程序正在关闭时,我在 C++ 中获得了一个无法轻易重现的 SEGV(它发生在大约 100,000 次测试运行中)。我检查了 errno 的值,它是零。这是在 Centos v4 上运行的。
pthread_join()
在什么条件下会获得 SEGV?这可能是某种竞争条件,因为它极为罕见。有人建议我不应该调用 pthread_detach() 和 pthread_exit(),但我不清楚为什么。
我的第一个工作假设是 pthread_join()
正在被调用,而 pthread_exit()
仍在另一个线程中运行,这会导致 SEGV,但许多人表示这不是问题。
在应用程序退出期间在主线程中获取 SEGV 的失败代码大致如下(为简洁起见省略了错误返回代码检查):
// During application startup, this function is called to create the child thread:
return_val = pthread_create(&_threadId, &attr,
(void *(*)(void *))initialize,
(void *)this);
// Apparently this next line is the issue:
return_val = pthread_detach(_threadId);
// Later during exit the following code is executed in the main thread:
// This main thread waits for the child thread exit request to finish:
// Release condition so child thread will exit:
releaseCond(mtx(), startCond(), &startCount);
// Wait until the child thread is done exiting so we don't delete memory it is
// using while it is shutting down.
waitOnCond(mtx(), endCond(), &endCount, 0);
// The above wait completes at the point that the child thread is about
// to call pthread_exit().
// It is unspecified whether a thread that has exited but remains unjoined
// counts against PTHREAD_THREADS_MAX, hence we must do pthread_join() to
// avoid possibly leaking the threads we destroy.
pthread_join(_threadId, NULL); // SEGV in here!!!
退出时加入的子线程运行以下代码,该代码从上面在主线程中调用releaseCond()
的点开始:
// Wait for main thread to tell us to exit:
waitOnCond(mtx(), startCond(), &startCount);
// Tell the main thread we are done so it will do pthread_join():
releaseCond(mtx(), endCond(), &endCount);
// At this point the main thread could call pthread_join() while we
// call pthread_exit().
pthread_exit(NULL);
线程似乎正常启动,在应用程序启动期间创建过程中没有产生错误代码,线程正确执行了它的任务,大约在应用程序退出前大约五秒钟。
什么可能导致这种罕见的 SEGV 发生,以及我应该如何针对它进行防御性编程。一种说法是我对 pthread_detach() 的调用是问题所在,如果是这样,我的代码应该如何更正。
【问题讨论】:
您是否检查了本地文档以了解您的 pthread_join() 并非所有都符合标准,您可能必须传递一个非 NULL 指针。 “子线程”如何知道何时调用该退出代码?该退出代码是如何被调用的? Centos 4?很复古。 LinuxThreads 还是 NPTL? @Loki man pthread_join 允许空指针。 @user315052 子线程知道因为主线程将状态变量设置为退出,然后释放启动条件,所以子线程将检查状态变量以查看请求的操作。为简洁起见,我省略了这一点。退出代码由从主线程管理子线程的对象的析构函数调用。 【参考方案1】:假设:
pthread_create
返回零(您正在检查它,对吗?)
attr
是一个有效的 pthread_attr_t
对象(你是如何创建它的?为什么不直接传递 NULL 呢?)
attr
没有指定线程要分离创建
您没有在其他地方的线程上调用pthread_detach
或pthread_join
...那么pthread_join
失败是“不可能的”,你要么有其他内存损坏,要么运行时出现错误。
[更新]
pthread_detach
的基本原理部分说:
*pthread_join*() 或 *pthread_detach*() 函数最终应该是 为创建的每个线程调用,以便存储关联 线程可能会被回收。
虽然没有说这些是互斥的,但pthread_join
documentation 指定:
如果线程指定的值,则行为未定义 *pthread_join*() 的参数不引用可连接线程。
我很难找到说明分离线程不可连接的确切措辞,但我很确定这是真的。
因此,请致电 pthread_join
或 pthread_detach
,但不能同时致电。
【讨论】:
1) 检查返回值是否为零。 2) 有效,使用pthread_attr_init(&attr);
和pthread_attr_setstacksize(&attr, stackSize);
到64 MB 堆栈大小。 3) Attr 中没有其他内容,所有其他内容均为默认值。 4) 在pthread_create()
之后不久,我确实 致电pthread_detach(_threadId);
。那么我应该如何更新来纠正这个问题呢?
你不需要加入一个分离的线程;这样做是错误的。回家后我会用参考更新我的答案...
我也很惊讶在 POSIX 文档(或至少一个更容易找到的文档)中没有更明确地描述什么是“可连接线程”。另一个可能相关的信息是在pthread_attr_getdetachstate()
的文档中:pubs.opengroup.org/onlinepubs/9699919799/functions/…,它说“如果线程是分离创建的,那么 pthread_detach() 或 pthread_join() 函数使用新创建的线程的 ID是一个错误”【参考方案2】:
如果您阅读了pthread_join 和pthread_exit 以及相关页面的标准文档,则连接会暂停执行“直到目标线程终止”,并且调用 pthread_exit 的线程在调用 pthread_exit 之前不会终止,那怎么办你担心的不是问题。
您可能在某处损坏了内存(如 Nemo 建议的那样),或从清理处理程序调用 pthread_exit(如 user315052 建议的那样),或其他原因。但这不是“pthread_join() 和 pthread_exit() 之间的竞争条件”,除非您使用的是错误或不兼容的实现。
【讨论】:
【参考方案3】:没有足够的信息来全面诊断您的问题。我同意其他发布的答案,即问题更可能是代码中未定义的行为,而不是pthread_join
和pthread_exit
之间的竞争条件。但我也同意这种竞争的存在会构成 pthread
库实现中的错误。
关于pthread_join
:
return_val = pthread_create(&_threadId, &attr,
(void *(*)(void *))initialize,
(void *)this);
//...
pthread_join(_threadId, NULL); // SEGV in here!!!
看起来加入是在一个类中。这开启了在main
尝试进行连接时删除对象的可能性。如果pthread_join
正在访问已释放的内存,则结果是未定义的行为。我倾向于这种可能性,因为访问释放的内存经常未被检测到。
关于pthread_exit
:Linux 上的手册页和 POSIX 规范状态:
当除第一次调用 main() 的线程之外的线程从用于创建它的启动例程返回时,会进行对 pthread_exit() 的隐式调用。函数的返回值作为线程的退出状态。
如果从作为隐式或显式调用 pthread_exit() 的结果而调用的取消清理处理程序或析构函数调用 pthread_exit() 的行为是未定义的。
如果pthread_exit
调用是在清理处理程序中进行的,您将有未定义的行为。
【讨论】:
是的,这是在一个类中,ctor 执行 pthread_create(),dtor 执行 pthread_join()。以上是关于为啥在 pthread_detach() 之后调用 pthread_exit() 在极少数情况下会导致 SEGV?的主要内容,如果未能解决你的问题,请参考以下文章
如果在已经返回的线程上调用pthread_detach会发生什么
linux下多线程之pthread_detach(pthread_self())
pthread_join和pthread_detach的用法