pthread_exit 函数和 pthread_join 函数
在进程中,若调用了函数 exit,_exit,或_Exit 时,则该进程会终止,同样,若进程中的线程调用这三个函数时也会使线程所在的进程终止。那么要是只是退出线程,而不终止线程所在的进程有什么办法?下面是在单线程模式下退出线程的三种方式(不会终止线程所在的进程):
- 线程只是从启动例程中返回,返回值是线程的退出码;
- 线程被同一进程的其他线程取消;
- 线程调用 pthread_exit 函数;
- /* 线程终止 */
- /*
- * 函数功能:退出线程;
- * 无返回值;
- * 函数原型:
- */
- #include <pthread.h>
- void pthread_exit(void *rval_ptr);
- /*
- * 说明:
- * rval_ptr是一个无类型指针,与传给启动例程的单个参数类似;
- * 进程中的其他线程可以通过调用pthread_join函数访问到这个指针;
- */
- /*
- * 函数功能:获取其他已终止的线程的退出码;
- * 返回值:若成功则返回0,否则返回错误编码;
- * 函数原型:
- */
- int pthread_join(pthread_t thread, void **rval_ptr);
- /*
- * 说明:
- * 调用pthread_join的线程将一直阻塞,直到thread指定的线程调用pthread_exit、从启动例程返回或被取消;
- * 如果线程只是从启动例程返回,rval_ptr将包含返回码;
- * 如果线程是被取消,由rval_ptr指向的内存单元设置为PTHREAD_CANCELED;
- */
调用 pthread_join 函数将自动分离指定的线程,被分离的线程就再也不能被其他线程连接了,即恢复了系统资源。若线程已处于分离状态,调用 pthread_join 会失败,将返回 EINVAL。所以,如果多个线程需要知道某个特定的线程何时结束,则这些线程应该等待某个条件变量而不是调用 pthread_join。
下面针对这两个函数进行测试:
- #include "apue.h"
- #include <pthread.h>
- static void *func1(void *arg);
- static void *func2(void *arg);
- int main(void)
- {
- pthread_t tid1, tid2;
- int err;
- void *tret;
- err = pthread_create(&tid1, NULL, func1, NULL);
- if(err != 0)
- err_quit("can't create thread 1: %s\n", strerror(err));
- err = pthread_create(&tid2, NULL, func2, NULL);
- if(err != 0)
- err_quit("can't create thread 2: %s\n", strerror(err));
- err = pthread_join(tid1,&tret);
- if(err != 0)
- err_quit("can't join with thread 1: %s\n", strerror(err));
- printf("thread 1 exit code %d\n", (int)tret);
- err = pthread_join(tid2,&tret);
- if(err != 0)
- err_quit("can't join with thread 2: %s\n", strerror(err));
- printf("thread 2 exit code %d\n", (int)tret);
- exit(0);
- }
- static void *func1(void *arg)
- {
- printf("thread 1 returning\n");
- return((void *)1);
- }
- static void *func2(void *arg)
- {
- printf("thread 2 exiting\n");
- pthread_exit((void*)2);
- }
输出结果:
- thread 1 returning
- thread 2 exiting
- thread 1 exit code 1
- thread 2 exit code 2
pthread_cancel 函数
在同一进程中,线程可以请求取消同一进程的其他线程, pthread_cancel 函数可以实现该功能。- /*
- * 函数功能:请求取消同一进程的其他线程;
- * 返回值:若成功则返回0,否则返回错误;
- * 函数原型:
- */
- int pthread_cancel(pthread_t tid);
- /*
- * 说明:
- * 在默认情况下,该函数会使得由tid标识的线程的行为表现为如同调用了参数为PTHREAD_CANCELED的pthread_exit函数,
- * 但是线程可以选择忽略取消方式或控制取消方式;
- * 注意:pthread_cancel并不等待线程终止,它仅仅提出请求;
- */
pthread_cleanup_push 函数和 pthread_cleanup_pop 函数
在进程中可以调用 atexit 函数注册在 main 结束后调用清理程序的函数。同样,线程也可以安排它退出时需要调用的清理处理程序函数。可以把每个线程考虑为有一个活动的清理处理程序函数的栈。调用 pthread_cleanup_push 将清理处理程序函数加到栈中,调用 pthread_cleanup_pop 删除最近添加的清理处理程序函数。当线程退出时,从最近添加的清理处理程序函数开始,各个活动的清理处理程序函数都将被调用,当所有清理处理程序函数返回时,线程被终止。当 pthread_cleanup_pop 中的参数 execute 为非 0 时,最近添加的清理处理程序函数也将被调用,然后删除。- /*
- * 函数功能:线程清理处理程序;
- * 无返回值;
- * 函数原型:
- */
- void pthread_cleanup_push(void(*rtn)(void *), void *arg);
- void pthread_cleanup_pop(int execute);
- /*
- * 说明:
- * 当线程执行以下动作时调用清理函数,调用参数为arg:
- * (1)调用pthread_exit函数时;
- * (2)响应取消请求时;
- * (3)用非零execute参数调用pthread_cleanup_pop时;
- * 如果参数execute设置为0,清理程序函数不被调用;
- */
- #include <pthread.h>
- #include <sys/types.h>
- #include "apue.h"
- static void cleanup(void *arg); //线程清理函数
- void *func1(void *arg);
- void *func2(void *arg);
- int main(void)
- {
- pthread_t tid1;
- pthread_t tid2;
- int err;
- void *tret;
- err = pthread_create(&tid1, NULL, func1, (void*)1);
- if(err != 0)
- err_quit("can't create thread 1: %s\n", strerror(err));
- err = pthread_create(&tid2, NULL, func2, (void*)1);
- if(err != 0)
- err_quit("can't create thread 2: %s\n", strerror(err));
- err = pthread_join(tid1,&tret);
- if(err != 0)
- err_quit("can't join with thread 1: %s\n", strerror(err));
- printf("thread 1 exit code %d\n", (int)tret);
- err = pthread_join(tid2, &tret);
- if(err != 0)
- err_quit("can't join with thread 2: %s\n", strerror(err));
- printf("thread 2 exit code %d\n",(int)tret);
- exit(0);
- }
- static void cleanup(void *arg)
- {
- printf("cleanup: %s\n", (char*)arg);
- }
- void *func1(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_cleanup_pop(0);
- pthread_cleanup_pop(0);
- return ((void*)1);
- }
- void *func2(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)
- pthread_exit((void*)2); //会调用清理处理程序
- pthread_cleanup_pop(0);
- pthread_cleanup_pop(0);
- pthread_exit((void*)2);
- }
- thread 1 start.
- thread 2 start.
- thread 2 push complete.
- cleanup: thread 2 second handler
- cleanup: thread 2 first handler
- thread 1 push complete.
- thread 1 exit code 1
- thread 2 exit code 2
pthread_detach 函数
在默认情况下,线程终止状态会保存到对该线程调用 pthread_join,如果线程已经处于分离状态,线程的底层存储资源可以在线程终止时立即被收回。当线程被分离时,并不能用 pthread_join 函数等待它的终止状态,对分离状态的线程进行 pthread_join 的调用会产生失败,返回 EINVAL。pthread_detach 调用可以用于线程进入分离状态。如果不想等待创建的某个线程,而且知道不再需要控制它,就可以调用 pthread_detach 来分离它。线程可以分离自己,任何知道其ID的其他线程也可以随时分离它。- /*
- * 函数功能:使线程处于分离状态;
- * 返回值:若成功则返回0,否则返回错误编码;
- * 函数原型:
- */
- int pthread_detach(pthread_t tid);
- #include <pthread.h>
- #include "apue.h"
- void *thr_fn(void *arg)
- {
- printf("thread start ...\n");
- printf("thread exiting...\n");
- pthread_exit((void *)1);
- }
- int main(void)
- {
- pthread_t tid1;
- int err;
- void* tret;
- err = pthread_create(&tid1, NULL, thr_fn, (void*)1);
- if(err != 0)
- err_quit("pthread_create error: %s\n", strerror(err));
- err = pthread_detach(tid1);
- if(err != 0)
- err_quit("pthread_detach error: %s\n", strerror(err));
- err = pthread_join(tid1,&tret);
- if(err != 0)
- err_quit("pthread_join error: %s\n", strerror(err));
- printf("thread 1 exit code:%d\n",(int)tret);
- exit(0);
- }
- thread start ...
- thread exiting...
- pthread_join error: Invalid argument
因为已经使用 pthread_detach 对线程进行分离,第二次调用 pthread_join 对线程进行分离时会返回 EINVAL。