函数简介:
头文件:#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 testfengpeng@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);
}