Linux_C编程—线程的终止

文章目录

  • 线程的终止情况
  • 使用return返回线程
  • 调用pthread_exit()终止线程
  • 非正常终止时清理线程资源
  • 例程

线程的终止情况

Linux中有俩种方式可以使线程正常终止:

  • 通过return从线程函数返回
  • 通过调用pthread_exit()函数,使线程退出

另外线程受到其他线程的干扰或者自身出错,都会导致线程非正常终止,而这种终止是不可预见的。
无论线程是正常终止还是非正常终止,都会涉及到一个资源释放的问题:

  • 在线程正常终止的情况下,能够自动释放线程的资源
  • 在线程非正常终止的情况下,调用pthread_cleanup_push()\pthread_cleanup_pop()俩个函数来进行线程清除工作

使用return返回线程

return我们在C语言的学习过程中已经很熟悉了,尤其是在必不可少的主函数中,最后加上return 0,在一个函数被调用的时候,只要执行到return,被调用的函数就会返回到主函数中。
所以,return具有返回调用函数到主函数的功能,在线程调用的函数中使用return就会使线程退出,退出到进程中。
但如果在main函数中调用return,整个进程将退出,进程中的线程也随之终止。

调用pthread_exit()终止线程

pthread_exit()函数是可以使线程正常终止的,使用线程中,当线程执行到pthread_exit()这一步时就会正常终止,并返回一个数据(指针)。
函数原型:

void pthread_exit( void *value_ptr );
  • value_ptr是一个指针,它可以储存线程的返回值,而且可以被pthread_join()函数的第二个参数接收。

非正常终止时清理线程资源

当线程非正常终止时,线程所拥有的资源是不会自动释放的,所以我们要调用清除函数,来清理线程资源。就是说,线程资源的清理是靠清理函数完成的,这个清理函数由用户编写。那么程序怎么知道线程是正常终止还是非正常终止呢?线程是如何调用线程清理函数的呢?

Linux系统提供了一对函数,用于检测线程是否非正常终止和调用线程清理函数,它们的函数原型如下:

void pthread_cleanup_push( void( *routine )( void* ), void *arg );
void pthread_cleanup_pop( int execute );
  • void( *routine )( void* )就是线程清理函数
  • void *arg就是要传入线程清理函数的参数
  • int execute有俩种取值:0和非0

使用时,将pthread_cleanup_push()pthread_cleanup_pop()分别放在可能出问题的代码的开头和结尾,这样就可以监控这一段代码了,如果在这一段代码中出现非正常终止或者调用pthread_exit()终止,都会触发清理函数,我们只需要把线程清理的操作编写在线程清理函数中即可。
还有一点,当pthread_cleanup_pop()的参数为 0 时,即使代码不触发清理函数,最后都会执行一次清理函数的。这就是参数int execute的作用,当不为0 时,如果没有触发清理函数,就不再执行清理函数。

设置多重线程清理函数时的执行顺序
还有,当在同一段代码上设置多重cleanup函数是,各个线程清理函数的执行顺序是先进后出式的执行,也就是从最里层的线程清理函数开始执行。这与pthread_cleanup_push()函数有关,push意为压、推,这个函数会就将调用的线程清理函数压入栈中,而栈中的数据都是先进后出的,所以着也就解释了多重线程清理函数的执行顺序了。

例程

通过下面一段代码来分析线程的终止的各种情况

#include "stdio.h"
#include "pthread.h"
#include "unistd.h"

/* 线程清理函数,这里的清理函数没有清理线程的功能,就是为了分析一下函数的执行 */
void *clean( void *arg )
{
	printf("cleanup:%s",arg);
	return ( void* )0;
}

/* 线程1 */
void *thread1( void *arg )
{
	printf("thread 1 start !\n");
	pthread_cleanup_push( ( void* )clean, "thread 1 first handler \n" );
	pthread_cleanup_push( ( void* )clean, "thread 1 second handler \n" );
	printf("thread 1 push complete \n");
	/* 线程1以return的方法返回 */
	if( arg )
	{
		return ( void* )1;
	}
	pthread_cleanup_pop( 0 );
	pthread_cleanup_pop( 0 );
	return ( void* )1;
}

/* 线程2 */
void *thread2( void *arg )
{
	printf("thread 2 start !");
	pthread_cleanup_push( ( void* )clean, "thread 2 first handler \n" );
	pthread_cleanup_push( ( void* )clean, "thread 2 second handler \n" );
	printf("thread 1 push complete \n");
	/* 线程2以调用pthread_exit()的方式终止 */
	if( arg )
	{
		pthrea_exit( ( void* )2 );
	}
	pthread_cleanup_pop( 0 );
	pthread_cleanup_pop( 0 );
	pthread_exit( ( void* )2 );
}

int main( void )
{
	pthread_t tid1,tid2;
	int error;
	/* 传递线程结束时返回的值 */
	void *tret;
	/* 创建线程1并传递参数 1 */
	error = pthread_create( &tid1, NULL, ( void* )thread1, ( void* )1 );
	if( error )
	{
		printf("thread 1 create failed \n");
		return -1;
	}
	/* 创建线程2并传递参数1 */
	error = pthread_create( &tid2, NULL, ( void* )thread2, ( void* )1 );
	if( error )
	{
		printf("thread 2 create failed \n");
		return -2;
	}
	/* 阻塞进程,执行线程1 */
	error = pthread_join( tid1, &tret );
	if( error )
	{
		printf("thread 1 join failed \n");
		return -1;
	}
	
	printf("thread 1 exit code is %d\n",( int )tret);
	/* 阻塞进程,执行线程2 */
	error = pthread_join( tid2, &tret );
	if( error )
	{
		printf("thread 2 join failed \n");
		return -2;
	}
	printf("thread 2 exit code is %d\n",( int )tret);
	return 1;
}

运行结果如下:
Linux_C编程—线程的终止_第1张图片
可以看出几点:

  • 第一:线程1返回的时候没有触发清理函数,说明return返回线程的情况不会触发清理函数
  • 第二:线程2的清理函数的执行顺序是,从最内层开始,符合栈的特点
  • 第三:pthread_exit()终止函数时会返回一个exit code值,可以在pthread_join()阻塞函数中读取这个值,传递到一个指针变量的地址中,然后打印出来

你可能感兴趣的:(Linux,线程)