5

我在 C++ 中获得了一个 SEGV,pthread_join()当我的应用程序关闭时,我在调用时无法轻松重现(它发生在大约 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() 的调用是问题所在,如果是这样,我的代码应该如何更正。

4

3 回答 3

4

假设:

  1. pthread_create返回零(您正在检查它,对吗?)
  2. attr是一个有效的pthread_attr_t对象(你是如何创建它的?为什么不直接传递 NULL 呢?)
  3. attr未指定要创建分离的线程
  4. 您没有在其他地方调用pthread_detach或在线程上pthread_join

...那么pthread_join失败是“不可能的”,并且您的运行时可能存在其他一些内存损坏或错误。

[更新]

理由部分pthread_detach说:

最终应该为每个创建的线程调用 *pthread_join*() 或 *pthread_detach*() 函数,以便可以回收与线程关联的存储。

尽管没有说这些是相互排斥的,但pthread_join文档指定:

如果 *pthread_join*() 的线程参数指定的值 不引用可连接线程,则行为未定义。

我很难找到说分离线程不可连接的确切措辞,但我很确定这是真的。

因此,要么调用pthread_join要么pthread_detach,但不能同时调用两者。

于 2012-07-11T22:52:25.417 回答
0

如果您阅读pthread_joinpthread_exit以及相关页面的标准文档,则连接会暂停执行“直到目标线程终止”,并且调用 pthread_exit 的线程在调用 pthread_exit 之前不会终止,所以您担心的是不是问题。

您可能在某处损坏了内存(如 Nemo 所建议的那样),或从清理处理程序调用 pthread_exit(如 user315052 所建议的那样),或其他原因。但这不是“pthread_join() 和 pthread_exit() 之间的竞争条件”,除非您使用的是错误或不兼容的实现。

于 2012-07-11T22:54:11.877 回答
0

没有足够的信息来全面诊断您的问题。我同意其他发布的答案,即问题更可能是代码中未定义的行为,而不是 和 之间的竞争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在清理处理程序中进行调用,您将有未定义的行为。

于 2012-07-11T23:06:05.133 回答