linux线程——pthread_exit()与pthread_join()

一、pthread_exit()
函数简介:

       头文件:#include <pthread.h>
       函数定义: void pthread_exit(void* retval);
       描述:
       线程通过这个函数来终止执行,就如同进程在结束时候调用exit函数一样。这个函数的作用是终止调用它的线程,并且返回一个指向某个对象的指针。由于一个进程中的多个线程是共享数据段的,因此通常在线程自己调用pthread_exit函数推出以后,退出线程所占用的资源并不能随着线程的终止而得到释放,还要配合使用那个pthread_join函数(下一部分)通过其他线程的同步并释放资源。
        参数:
        retval:这是函数的唯一的参数——是函数的返回代码。(返回的内容存储在void *retval中)
        只要pthread_exit中的参数retval不是NULL,这个值将被传递给 thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH。

一个比较好的例子:
#include <stdio.h>
#include <pthread.h>
/*线程1*/
void* thread_1(void* arg)
{
	int i;
	for(i=0;i<=6;i++)
	{
		printf("This is a pthread_1.\n");
		if(i==2)
		  pthread_exit((void*)1);//使用pthread_exit不会使得线程资源释放
		sleep(1);
	}
}
/*线程2*/
void* thread_2(void *arg)
{
	int i;
	for(i=0;i<3;i++)
	{
		printf("This is a pthread_2.\n");
	}
	pthread_exit((void *)2);//使用pthread_exit不会使得线程资源释放
}
int main(void)
{
	pthread_t id_1,id_2;
	int i,ret;
	void *rval_ptr;
	/*创建线程一*/
	ret=pthread_create(&id_1,NULL,thread_1,NULL);
	if(ret!=0)
	{
		printf("Create pthread error!\n");
		return -1;
	}
	/*创建线程二*/  
     ret=pthread_create(&id_2,NULL,thread_2,NULL);  
    if(ret!=0)  
    {  
        printf("Create pthread error!\n");  
		return -1;
    }  
    /*等待线程结束*/  
    pthread_join(id_1,&rval_ptr);  
	printf("pthread_join 1 :%d\n",(int)rval_ptr);
    pthread_join(id_2,&rval_ptr);
	printf("pthread_join 2 :%d\n",(int)rval_ptr);
    return 0;  
}  

二、 pthread_join()

函数简介:
       头文件:#include <pthread.h>
       函数定义: int pthread_join(pthread_t thread,void **retval);
       描述:
       pthread_join()函数,是以阻塞的放肆和来等待thread指定的线程结束。当函数返回时候,被等待线程的资源被回收。如果该线程之前已经结束了,那么该函数会立即返回。并且thread指定的线程必须是joinable的。
     参数:

       thread:线程标识符,即线程ID,标识唯一的线程。这个参数用来说明将要等待线程的ID

      retval:用户定义的指针,用来存储被等待线程的返回值。(返回的内容存储在void *retval中)

      返回值:0代表返回成功。失败,返回的则是错误号。

函数应用

       linux中,默认的情况下是在一个线程被创建以后,必须用此函数对创建的线程进行资源回收,但是可以设置Threadsattributes来设置一个线程结束时,直接收回此线程所占用的系统资源。

       同时在linux中,新建的线程并不是在原先的进程之中,而是系统通过一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程,并且在这个进程中执行线程函数。不过这个copy的过程和fork不一样。copy后的进程和原来的进程共享了所有的变量,运行环境。这样,原来进程中的变量变动在copy后的进程中能够体现出来。

       pthread_join使得一个线程等待另一个线程结束,如果main函数中没有phtread_join,主线程会很快结束从而使得整个进程结束,从而使得创建的线程没有机会执行就结束了。加入pthread_join以后,主线程会一直等待直到等待的线程自己结束以后才结束,使得创建的线程又机会执行。

      
三、pthread_exit与pthread_join两者的关系

pthread_join一般是主线程来调用,等待子线程退出,因为是等待,所以是阻塞的,一般主线程会依次join所有它创建的子线程。

pthread_exit一般来说是子线程调用,用来结束当前线程。

子线程可以通过pthread_exit传递一个返回值,而主线程可以通过pthread_join获得该返回值,从而判断该子线程的退出是正常还是异常。


四、使用范例

#include "apue.h"
#include <pthread.h>
void* thr_fn1(void *arg)
{
	printf("thread1 returning!\n");
	return ((void*)1);
}
void* thr_fn2(void *arg)
{
	printf("thread 2 exiting!\n");
	pthread_exit((void*)2);
}
int main(void)
{
	int err;
	pthread_t tid1,tid2;
	void *tret;//用于存储pthread_join中得到的返回值

	err=pthread_create(&tid1,NULL,thr_fn1,NULL);
	if(err!=0)
	  err_quit("can't create thread 1: %s\n",strerror(err));

	err=pthread_create(&tid2,NULL,thr_fn2,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);
}
fengpeng@ubuntu:~/workspace/work1$ gcc helloworld.c -pthread -o test
fengpeng@ubuntu:~/workspace/work1$ ./test
thread 2 exiting!
thread1 returning!
thread 1 exit code 1

thread 2 exit code 2


五、使用注意

pthread_create和pthread_exit函数的无类型指针参数能够传递的数值可以不止一个,该指针可以传递包含更加复杂信息的结构的地址,但是注意这个结构所使用的内存在调用完成调用以后,必须仍然有效,否则就会出现无效或者非法内存。

例如,线程在自己的栈上分配了一个结构体用来用作返回值,它把这个指向结构的指针传递给pthread_exit(),那么当主线程在调用pthread_join的时候,之前的线程已经被撤销了,这块分配结构体的内存也另作他用了。

#include "apue.h"
#include <pthread.h>
struct foo {
	int a,b,c,d;
};
void printfoo(const char *s,const struct foo *fp)//按照地址传递
{
	printf(s);
	printf(" stucture at 0x%x\n",(unsigned)fp);
	printf(" foo.a=%d\n",fp->a);
	printf(" foo.b=%d\n",fp->b);
	printf(" foo.c=%d\n",fp->c);
	printf(" foo.d=%d\n",fp->d);
}

void* thr_fn1(void *arg)
{
	struct foo foo={1,2,3,4};//线程的自动变量会分配在栈中
	printfoo("thread 1:\n",&foo);
	pthread_exit((void*)&foo);//线程的返回值存储在foo中(分配在栈中),在线程退出后会自动释放。
}

void* thr_fn2(void *arg)
{
	printf("thread 2 : ID is %d\n",pthread_self());
	pthread_exit((void*)0);
}

int main(void)
{
	int err;
	pthread_t tid1,tid2;
	struct foo *fp;
	err=pthread_create(&tid1,NULL,thr_fn1,NULL);
	if(err!=0)
	  err_quit("can't create thread 1: %s\n",strerror(err));
	err=pthread_join(tid1,(void*)&fp);//fp存储 被等待线程 的返回值
	if(err!=0)
	  printf("can't join thread1.\n");
	sleep(1);
	
	err=pthread_create(&tid2,NULL,thr_fn2,NULL);
	if(err!=0)
	{
		printf("create thread 2 is failed.\n");
		exit(1);
	}
	sleep(1);
	printfoo("parent:\n",&fp);//fp的内容是thr_fn1中的&foo,已经被释放了,此处打印的是野指针的内容
	exit(0);
}





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值