7.线程回收
两种方法回收线程
- 使用pthread_join 函数
- 使线程分离
1.pthread_join 函数
原型:
#include <pthread.h> int pthread_join(pthread_t thread, void **retval);
功能:等待一个线程的结束
参数:
thread
:需要等待的线程ID。retval
:指向一个指针,用于存储被等待线程的返回值。如果不关心返回值,可以设置为NULL。返回值:
- 成功时,返回0;
- 失败时,返回一个非零的错误码。
注意:
pthread_join
是阻塞函数,如果回收的线程没有结束,则一直等待示例:
创建了一个子线程,并在子线程中执行一个函数fun。主线程等待子线程结束后,打印子线程的返回值。
#include <stdio.h> #include <pthread.h> #include <unistd.h> void *fun(void * arg) { int i; pthread_detach(pthread_self()); for(i=0; i<3; i++) { printf("child %d\n",i); sleep(1); } pthread_exit("fun return"); } int main(int argc, const char *argv[]) { int i = 0; void *retf; pthread_t tid; pthread_create(&tid,NULL,fun,(void *)i); pthread_join(tid,&retf);//等待线程结束 printf("%s\n",(char*)retf); return 0; }
2.线程分离
线程分离是将线程设置为不被其他线程等待或回收的状态,从而在退出时自动释放资源。
- 如果不需要关心线程的返回值,或者不希望等待线程结束,可以将线程设置为分离状态。这样,线程在结束时会自动释放其所占用的资源,无需进行
pthread_join
操作,从而减少了程序的负担。 - 注意:一旦线程被分离,就不能再被其他线程回收,也就不能调用
pthread_join
。因此,在决定分离线程之前,应该仔细考虑线程的用途和生命周期。
两种方式使线程分离
- 使用
pthread_detach
函数 - 使用
pthread_attr_init
函数和pthread_attr_setdetachstate
函数,设置线程属性为分离
pthread_detach 函数
-
原型:
#include <pthread.h> int pthread_detach(pthread_t thread);
-
功能:将一个线程设置为分离状态,线程结束后(不会产生僵尸线程)
-
参数:
thread
:需要设置为分离状态的线程ID。 -
返回值:
- 成功时,返回0;
- 失败时,返回一个非零的错误码
-
示例:
#include <stdio.h> #include <pthread.h> #include <unistd.h> void *fun(void * arg) { pthread_detach(pthread_self());//分离线程 printf("child %d\n",(int)arg); pthread_exit(NULL); } int main(int argc, const char *argv[]) { int i; pthread_t tid[10]; for(i=0; i<10; i++)//创建十个线程 { pthread_create(&tid[i],NULL,fun,(void *)i); } sleep(10);//等待线程运行 return 0; }
设置线程属性为分离
pthread_attr_init函数
原型:
int pthread_attr_init(pthread_attr_t *attr);
功能:初始化一个线程属性对象
参数:
attr
:指向要初始化的线程属性对象的指针。返回值:
- 成功时,返回0;
- 失败时,返回一个非零的错误码。
pthread_attr_setdetachstate函数
原型:
#include <pthread.h> int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
功能:设置线程属性对象的分离状态
参数:
attr
:指向要设置分离状态的线程属性对象的指针。
detachstate
:指定线程的分离状态,可以是PTHREAD_CREATE_JOINABLE或PTHREAD_CREATE_DETACHED。返回值:
- 成功时,返回0;
- 失败时,返回一个非零的错误码。
示例:该线程在创建时就是分离的线程
pthread_attr_t attr;//定义线程属性变量 pthread_attr_init(&attr);//初始化该变量 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//设置属性为分离 pthread_create(&tid,&attr,fun,NULL);//创建线程
可以通过top命令查看进程占用的内存空间大小,检验线程是否被回收
注意:分离后,进程结束,线程也会随之结束。
取消线程
pthread_cancel 函数
原型:
#include <pthread.h> int pthread_cancel(pthread_t thread);
功能:取消一个线程的执行
参数:
thread
:要取消执行的线程ID。返回值:
- 成功时,返回0;
- 失败时,返回一个非零的错误码。
注意:线程的取消要有取消点才可以,不是说取消就取消,线程的取消点主要是阻塞的系统调用如sleep();
若没有取消点,可手动设置一个取消点
pthread_testcancel 函数
原型:
#include <pthread.h> void pthread_testcancel(void);
功能:在当前线程中创建一个取消点,这样如果该线程收到取消请求时,它可以在这个点上响应并执行相应的取消操作。
pthread_setcancelstate函数
原型:
#include <pthread.h> int pthread_setcancelstate(int state, int *oldstate);
功能:设置线程的取消状态
参数:
state
:指定线程的取消状态,可以是PTHREAD_CANCEL_ENABLE或PTHREAD_CANCEL_DISABLE。
oldstate
:指向一个整型变量的指针,用于保存线程原来的取消状态。返回值:
- 成功时,返回0;
- 失败时,返回一个非零的错误码。
pthread_setcanceltype函数
原型:
#include <pthread.h> int pthread_setcanceltype(int type, int *oldtype);
功能:设置线程的取消类型
参数:
type
:指定线程的取消类型,可以是PTHREAD_CANCEL_DEFERRED(等到取消点才取消)、PTHREAD_CANCEL_ASYNCHRONOUS(目标线程会立即取消)
oldtype
:指向一个整型变量的指针,用于保存线程原来的取消类型。返回值:
- 成功时,返回0;
- 失败时,返回一个非零的错误码。
示例:线程设置为可取消后,被取消,线程结束
#include <stdio.h> #include <pthread.h> #include <unistd.h> void *fun(void * arg) { int i; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);//设置线程为不能取消 for(i=0; i<10; i++) { printf("child %d\n",i); sleep(1); } pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);//设置线程为可以取消 for(i=10; i<20; i++) { printf("child %d\n",i); sleep(1); } pthread_exit(NULL); } int main(int argc, const char *argv[]) { int i = 0; pthread_t tid; pthread_create(&tid,NULL,fun,(void *)i); sleep(5);//等待五秒 pthread_cancel(tid);//取消线程 while(1) { sleep(1); } return 0; }
线程的清理
当线程非正常终止,需要清理一些资源。
pthread_cleanup_push函数
原型:
#include <pthread.h> void pthread_cleanup_push(void (*routine)(void *), void *arg);
功能:将一个清理函数压入线程的清理栈中
参数:
routine
:指向要压入清理栈中的清理函数的指针。
被pthread_cancel取消掉。
执行pthread_exit
非0参数执行pthread_cleanup_pop()
arg
:传递给清理函数的参数。
pthread_cleanup_pop函数
原型:
#include <pthread.h> void pthread_cleanup_pop(int execute);
功能:用来解除之前通过
pthread_cleanup_push
注册的线程清理函数参数:
execute
:一个整数,表示是否执行清理函数。如果为非零值,则执行清理函数;如果为零,则不执行。
注意:
必须成对使用,即使pthread_cleanup_pop不会被执行到也必须写上,否则编译错误。
pthread_cleanup_pop()被执行且参数为0,pthread_cleanup_push回调函数routine不会被执行.
pthread_cleanup_push 和pthread_cleanup_pop可以写多对,routine执行顺序正好相反
线程内的 return 可以结束线程,也可以给pthread_join返回值,但不能触发pthread_cleanup_push里面的回调函数,所以我们结束线程尽量使用pthread_exit退出线程。