1.进程中的任意线程调用了exit、_Exit 或者 _exit,那么整个进程就会终止。
2.如果默认的动作是终止进程,那么发送到线程的信号也会终止整个进程。
在不终止进程的情况下停止单个线程:
1.线程可以从启动例程中返回(return),返回值是线程的退出码。
2.线程可以被同一进程中的其他线程取消。
3.线程调用pthread_exit。
#include < pthread. h>
void pthread_exit( void *rval_ptr);
//结束线程
rval_ptr 参数是一个无类型指针,保存线程的退出码。
#include < pthread. h>
int pthread_join( pthread_t thread, void **rval_ptr);
//返回值:若成功,返回0;否则,返回错误编码。
调用该函数的线程将一直阻塞,等待直到指定的线程终止。
如果线程简单地从它的启动例程返回,rval_ptr就包含返回码。如果线程被取消,由rval_ptr指定的内存单元就被设置为PTHREAD_CANCELED,如果不需要知道线程的返回值,则可以设置为NULL。
可以通过调用该函数来使线程置于分离状态,这样在线程结束后,所使用的资源将得到恢复。
如果线程已经处于分离状态,pthread_join调用就会失败,返回EINVAL。
#include
#include
#include
#include
void *thr_fn1(void *arg){
printf("thread 1 returning\n");
return ((void *)1);
}
void *thr_fn2(void *arg){
printf("thread 2 exiting\n");
pthread_exit((void *)2);
}
int main(int argc, const char * argv[]) {
int err;
pthread_t tid1,tid2;
void *tret;
err = pthread_create(&tid1, NULL, thr_fn1, NULL);//创建子线程1
if (err != 0) {
printf("can't create thread 1");
exit(-1);
}
err = pthread_create(&tid2, NULL, thr_fn2, NULL);//创建子线程1
if (err != 0) {
printf("can't create thread 2");
exit(-1);
}
err = pthread_join(tid1, &tret); //阻塞等待线程1结束
if (err != 0) {
printf("can't join with thread 1");
exit(-1);
}
printf("thread 1 exit code %ld\n",(long)tret);
err = pthread_join(tid2, &tret);//阻塞等待线程2结束
if (err != 0) {
printf("can't join with thread 2");
exit(-1);
}
printf("thread 2 exit code %ld\n",(long)tret);
exit(0);
}
运行结果:
thread 1 returning
thread 2 exiting
thread 1 exit code 1
thread 2 exit code 2
Program ended with exit code: 0
#include < pthread. h>
int pthread_cancel( pthread_t tid);
//返回值:若成功,返回0;否则,返回错误码。
线程可以通过调用pthread_cancel来取消同一进程中的其他线程。
pthread_cancel函数会使得由tid标识的线程的行为表现为如同调用参数PTHREAD_CANCELED的pthread_exit函数,但是,线程可以选择忽略取消或者控制如何被取消。
pthread_cancel不等待线程终止,它仅仅提出要求。
线程可以安排它退出时需要调用的函数,这样的函数称为线程清理处理程序。
一个线程可以安排多个线程清理处理程序,这些处理程序记录在栈中,也就是说,它们的执行顺序与它们注册时相反。
#include < pthread. h>
void pthread_cleanup_push( void (*rtn)( void *), void *arg);
void pthread_cleanup_pop( int execute);
当线程执行以下动作时,清理函数rtn是由pthread_ cleanup_ push函数调度的,调用时,只有一个参数arg:
1.调用pthread_exit时;
2.响应取消请求时;
3.用非零execute参数调用pthread_ cleanup_ pop函数时;
如果execute参数为0,清理函数将不被调用。
不管发生上述哪种情况,pthread_cleanup_pop都将删除上一次pthread_cleanup_push调用建立的清理函数,pthread_cleanup_pop一次只清理一个,所以一般在使用时要pthread_cleanup_push和pthread_cleanup_pop成对出现,否则有可能编译不通过。
#include
#include
#include
#include
void cleanup(void *arg)
{
printf("cleanup: %s\n",(char *)arg);
}
void *thr_fn1(void *arg)
{
printf("thread 1 start\n");
pthread_cleanup_push(cleanup, "thread 1 first handler");
pthread_cleanup_push(cleanup, "thread 1 second handler");
printf("thread 1 push complete\n");
if (arg) {
//return ((void *)1);
pthread_exit((void *)1);//触发清理函数
}
//一般pop和push是成对出现的,因为有些平台是用宏定义的函数,可能定义时push后面多带了个‘{’,而pop后面多带了一个‘}’,所以用0作为参数的pop和push进行配对
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
return ((void *)1);
}
void *thr_fn2(void *arg)
{
printf("thread 2 start\n");
pthread_cleanup_push(cleanup, "thread 2 first handler");
pthread_cleanup_push(cleanup, "thread 2 second handler");
printf("thread 2 push complete\n");
if (arg) {
//return ((void *)2);
pthread_exit((void *)2);//触发清理函数
}
//一般pop和push是成对出现的,因为有些平台是用宏定义的函数,可能定义时push后面多带了个‘{’,而pop后面多带了一个‘}’,所以用0作为参数的pop和push进行配对
pthread_cleanup_pop(0);
pthread_cleanup_pop(0);
return ((void *)2);
}
int main(void)
{
int err;
pthread_t tid1,tid2;
void *tret;
err = pthread_create(&tid1, NULL, thr_fn1, (void *)1);
if (err != 0) {
printf("can't create thread 1");
exit(-1);
}
err = pthread_create(&tid2, NULL, thr_fn2, (void *)1);
if (err != 0) {
printf("can't create thread 2");
exit(-1);
}
err = pthread_join(tid1, &tret);
if (err != 0) {
printf("can't join with thread 1");
exit(-1);
}
printf("thread 1 exit code %ld\n",(long)tret);
err = pthread_join(tid2, &tret);
if (err != 0) {
printf("can't join with thread 2");
exit(-1);
}
printf("thread 2 exit code %ld\n",(long)tret);
exit(0);
}
执行结果:
thread 1 start
thread 1 push complete
cleanup: thread 1 second handler
cleanup: thread 1 first handler
thread 1 exit code 1
thread 2 start
thread 2 push complete
cleanup: thread 2 second handler
cleanup: thread 2 first handler
thread 2 exit code 2
Program ended with exit code: 0
后push的清理函数先执行,栈式记录。
pthread_cleanup_push和pthread_cleanup_pop之间不能调用retrn,否则有可能出现段错误而使程序崩溃。
因为某些平台下这两个函数是用宏定义实现的,宏把某些上下文存放到栈上,而清理函数也是被放到栈上。当线程1在调用pthread_cleanup_push和调用pthread_cleanup_pop之间返回时,栈已被改写(return后相应的函数就得出栈,栈被改变),而再次调用清理函数时就使用了这个被改变的栈,上下文内容被破坏,就出现段错误崩溃,所以使用清理函数的线程结束时应该调用pthread_exit而不应该使用return。
默认情况下,线程的终止状态会保存直到对该线程调用pthread_join。如果线程已经被分离,线程的底层可以在线程终止时立即被收回。
在线程分离后,我们不能使用pthread_join等待线程的终止状态,因为对分离的线程调用pthread_join会产生未定义的行为。
可以调用pthread_ detach分离线程。
#include < pthread. h>
int pthread_ detach( pthread_ t tid);
//返回值:若成功,返回0;否则返回失败编码。