线程退出的三种情况:第一种是进程结束,进程中所有的线程也会随之结束。第二种是通过函数 pthread_exit 来主动的退出线程。第三种被其他线程调用 pthread_cancel 来被动退出。
当线程结束后,主线程可以通过函数 pthread_join/pthread_tryjoin_np来回收线程的资源,并且获得线程结束后需要返回的数据。因为在Linux系统中程序的线程资源是有限的,表现为对于一个程序其能同时运行的线程数是有限的。而默认的条件下,一个线程结束后,其对应的资源不会被释放,于是,如果在一个程序中,反复建立线程,而线程又默认的退出,则最终线程资源耗尽,进程将不再能建立新的线程。因此,我们在使用线程的过程中,对于已经创建的资源一定要合理的回收,防止资源泄露。
1、线程主动退出(pthread_exit () 函数的使用):
#include
void pthread_exit(void *retval);
pthread_exit () 函数为线程退出函数,在退出时候可以传递一个 void* 类型的数据带给主线程,若选择不传出数据,可将参数填充为 NULL。
2、线程被动退出(pthread_cancel ()):
#include
int pthread_cancel(pthread_t thread);
该函数传入一个 tid 号,会强制退出该 tid 所指向的线程,若成功执行会返回 0。
使用以上两个函数进行线程的退出,但是相对应的资源(子线程创建时从父线程copy出来的栈内存;子线程内部单独申请的堆内存(malloc、realloc、calloc)和锁资源mutex)并不会被回收,为了防止资源的过度占用造成内存泄漏,在线程回收的时候,或者当线程处于加锁后解锁前的状态时,应当采取相应的措施来回收该线程资源。
3、线程资源回收(阻塞的方式 pthread_join ())
线程资源回收(阻塞)
#include
int pthread_join(pthread_t thread, void **retval);
使用该函数对线程资源进行回收,默认采用阻塞的方式,直到成功回收后才会返回,第一个参数为要回收线程的 tid 号,第二个参数为线程回收后接受线程传出的数据。
4、线程资源回收( 非阻塞方式 pthread_tryjoin_np () )
线程资源回收(非阻塞)
#define _GNU_SOURCE
#include
int pthread_tryjoin_np(pthread_t thread, void **retval);
该函数为非阻塞模式回收函数,通过返回值判断是否回收掉线程,成功回
收则返回 0,其余参数与 pthread_join 一致
线程分为可结合的(joinable)和 分离的(detached)两种,如果没有在创建线程时设置线程的属性为PTHREAD_CREATE_DETACHED,则线程默认是可结合的,以上方式使用的均为可结合线程。可结合的线程在线程退出后不会立即释放资源,必须要调用pthread_join来显式的结束线程。分离的线程在线程退出时系统会自动回收资源。
3/4 .1 如何设置分离线程?
1.在创建线程时加上
pthread_attr_t attr;
pthread_t thread;
pthread_attr_init (&attr);
/* 设置线程的属性为分离的 */
pthread_attr_setdetachstat(&attr, PTHREAD_CREATE_DETACHED);
pthread_create (&thread, &attr, &thread_function, NULL);
/* 销毁一个目标结构,并且使它在重新初始化之前不能重新使用 */
pthread_attr_destroy (&attr);
2.在线程中调用pthread_detach(pthread_self());
3.主线程中调用pthread_detach(pid),pid为子线程的线程号
要注意的是:设置为分离的线程是不能调用pthread_join的,调用后会出错。
同时,pthread_create 创建线程时,若不指定分配堆栈大小,系统会分配默认值,查看默认值方法如下:
# ulimit -s
8192
#
上述表示为8M;单位为KB。
在嵌入式中内存不是很大,若采用默认值的话,会导致出现问题,若内存不足,则 pthread_create 会返回 12。在此就不再添加如何重新设置堆栈内容,需要使用到的时候可以去如下链接查找相关代码。其中如上部分内容也来自该博文。
链接地址:Linux线程退出、资源回收、资源清理的方法 - CTHON - 博客园 (cnblogs.com)
例程 1:
#例程 01
#include
#include
void* func1(void* arg)
{
static int str = 0; //作为静态局部变量,将存入全局变量的内存空间,该函数结束调用后,
//参数的内存空间不会被释放。
str = *(int*)arg;
str++;
printf("func1: %d,addr:%p\n",str,&str);
pthread_exit((void*)&str);
}
int main()
{
int ret;
int *tmp;
int a = 10;
pthread_t tid1;
pthread_create(&tid1,NULL,func1,(void*)&a);
pthread_join(tid1,(void*)&tmp);
printf("tmp get :%d,addr:%p\n",*tmp,tmp);
return 0;
}
例程 2:
#define _GNU_SOURCE
#include
#include
#include
void* func(void* arg)
{
printf("Pthread:%d Come !\n",(int)(long)arg+1);
pthread_exit((void*)arg);
}
int main()
{
pthread_t array[3];
int str;
int ret;
int i;
int flag = 0;
for(i = 0;i < 3;i++){
ret = pthread_create(&array[i],NULL,func,(void*)(long)i);//
if(ret != 0){
printf("create %d thread failed\n",i);
perror("Why:\n");
return -1;
}
}
while(1){
for(i = 0;i < 3;i++){
if(pthread_tryjoin_np(array[i],(void*)&str) == 0){
printf("%d thread exit\n",(int)str + 1);
flag++;
}
}
if(flag >= 3){
break;
}
}
return 0;
}
分析:循环创建3个线程,并且将其 i 的顺序作为参数传入线程程序,在主函数中采用非阻塞方式回收线程资源,并且设置 flag 标志位,当三个线程资源全部被回收的时候,主函数结束。
例程 3 :
#define _GNU_SOURCE
#include
#include
#include
void* func1(void* arg)
{
printf("This is pthread 1,come!\n");
while(1){
sleep(1);
}
}
void* func2(void* arg)
{
printf("This is pthread 2,come!\n");
pthread_cancel((pthread_t)arg);
pthread_exit((void*)pthread_self());
}
int main()
{
int ret;
int i;
int str;
pthread_t array[2];
ret = pthread_create(&array[0],NULL,func1,NULL);
if(ret != 0){
printf("create first pthread failed\n");
perror("Why:\n");
return -1;
}
pthread_create(&array[1],NULL,func2,(void*)array[0]);
if(ret != 0){
printf("create first pthread failed\n");
perror("Why:\n");
return -1;
}
int flag = 0;
while(1){
for(i = 0;i < 2;i++){
if(pthread_tryjoin_np(array[i],(void*)&str) == 0){
printf("%d exit\n",str);
flag++;
}
}
if(flag == 2) break;
}
return 0;
}
分析:首先创建了线程1,其一创建就会处于睡眠状态,只有等到线程2创建后执行线程1强制退出后,才会结束该线程,随后主函数通过非阻塞的方式进行回收资源。