线程退出的三种情况:第一种是进程结束,进程中所有的线程也会随之结束。第二种是通过函数 pthread_exit 来主动的退出线程。第三种被其他线程调用 pthread_cancel 来被动退出。

当线程结束后,主线程可以通过函数 pthread_join/pthread_tryjoin_np来回收线程的资源,并且获得线程结束后需要返回的数据。因为在Linux系统中程序的线程资源是有限的,表现为对于一个程序其能同时运行的线程数是有限的。而默认的条件下,一个线程结束后,其对应的资源不会被释放,于是,如果在一个程序中,反复建立线程,而线程又默认的退出,则最终线程资源耗尽,进程将不再能建立新的线程。因此,我们在使用线程的过程中,对于已经创建的资源一定要合理的回收,防止资源泄露。

1、线程主动退出(pthread_exit () 函数的使用):

#include <pthread.h>

void pthread_exit(void *retval);

pthread_exit () 函数为线程退出函数,在退出时候可以传递一个 void* 类型的数据带给主线程,若选择不传出数据,可将参数填充为 NULL。

2、线程被动退出(pthread_cancel ()):

#include <pthread.h>

int pthread_cancel(pthread_t thread);

该函数传入一个 tid 号,会强制退出该 tid 所指向的线程,若成功执行会返回 0。

使用以上两个函数进行线程的退出,但是相对应的资源(子线程创建时从父线程copy出来的栈内存;子线程内部单独申请的堆内存(malloc、realloc、calloc)和锁资源mutex)并不会被回收,为了防止资源的过度占用造成内存泄漏,在线程回收的时候,或者当线程处于加锁后解锁前的状态时,应当采取相应的措施来回收该线程资源。

3、线程资源回收(阻塞的方式 pthread_join ())

线程资源回收(阻塞)

#include <pthread.h>

int pthread_join(pthread_t thread, void **retval);

使用该函数对线程资源进行回收,默认采用阻塞的方式,直到成功回收后才会返回,第一个参数为要回收线程的 tid 号,第二个参数为线程回收后接受线程传出的数据。

4、线程资源回收( 非阻塞方式 pthread_tryjoin_np () )

线程资源回收(非阻塞)

#define _GNU_SOURCE
#include <pthread.h>

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 <stdio.h>
#include <pthread.h>

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 <stdio.h>
#include <pthread.h>
#include <unistd.h>

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 <stdio.h>
#include <pthread.h>
#include <unistd.h>

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强制退出后,才会结束该线程,随后主函数通过非阻塞的方式进行回收资源。