注意:多线程相关的代码,在编译时必须加-lpthread或者-pthread选项,例如:
gcc thread_test.c -o test_exe -pthread
一、创建线程:pthread_create
原型:
int pthread_create(pthread_t *tid,
const pthread_attr_t *attr,
(void*)(*start_rtn)(void* arg),
void *arg);
形参:tid输出所成功创建的新线程的线程ID,tid=thread id;
attr设定所创建的线程参数,传入NULL意味着使用默认参数;
start_rtn线程入口函数,函数格式形如: void *thread_entry(void *para);
arg创建新线程时,要传给入口函数的参数;
注意:通过本函数把参数arg传给新线程时,是需要一定时间的(确切的说,是创建线程这一过程较为耗时),那么从执行完本函数开始,一直到新线程被创建完成,且新线程读完arg参数之前,主线程中实参arg指向的内容不允许变,否则,新线程读到的启动参数就不对了,这一问题主要出现在类似这种代码中:
pthread_t tid[5];
for( i = 0; i < 5; i++)
{
stat = pthread_create(&tid[i], NULL, test_func, &i);
````//其余代码
}
感觉上,创建的5个线程,会在各自的入口函数中,依次收到pthread_create的第4个实参:0、1、2、3、4,而实际上,这5个线程收到的却都是5,原因就在于,进程尚未读到这个第四实参,i的值就被改变了。
这一问题一般有3个解决方法:
① 本函数返回后,sleep一段时间,目的是线程创建完毕,且读完arg参数,
② 为每一个线程用不同的变量传递参数,而不是跟这个例子似的,用同一个变量i;
③ 这种方法仅适用于传递的数据的字节数≤sizeof(指针)时,换句话说就是,本来传参是通过传递个地址给线程,让线程从这个地址中读出真正的参数,显然,这也是引发上述问题的根源,这个地址处的值可能会改变。但是,pthread_create传递的第4实参,指针本身的值不会变,也即,我们可以投机取巧,把这个指针本身的值作为参数传给新线程,这个值无论如何不会出错的。也即,在线程入口函数void* start_rtn(void* arg)中,读*arg的值可能会变,*arg的值可能会被主线程给修改,但是arg本身的值,是不会变的。
二、线程退出:pthread_exit
原型:void pthread_exit(void* retval);
功能:立即退出线程,如果退出时还想返回一些数据供父线程处理/使用,那么这些数据可以通过函数形参retval传出,传出的值可通过pthread_join函数获得。
注意:
1、我们传出的是数据的地址,而不是数据本身,因此数据本身必须是静态的(如果多个线程都用同一个入口函数,这方法就不行了)、或者从内存堆动态分配的,而决不能是函数中的动态临时变量,因为一旦线程入口函数返回,那么栈中的数据值就消失/不可用了
2、在线程的入口函数中执行return p;和执行pthread_exit(p);相同点是:都可以退出线程,都可以传出返回值,而且返回值都可以被pthread_join捕获。
区别在于:return p只能在入口函数中退出线程,而pthread_exit(p)可以在入口函数的子函数中退出线程;return p不会触发线程清理工作,pthread_exit(p)可以触发线程清理工作,详情请参考另一篇博文:线程取消与清理。
三、阻塞地等待某个线程退出pthread_join
原型:int pthread_join(pthread_t tid, void **retval);
功能:阻塞地等待某个线程退出,另外注意:父线程创建子线程之后,父线程会维护一些子线程的信息,只有显式地调用了该函数,父进程才会在子线程结束后清理所维护的子线程信息,换句话说,如果父线程不调用该函数,那么大量的子线程信息得不到释放,可能会造成堆溢出,除非:我们把创建的子线程与父线程分离开,这样子线程的信息在子线程结束后自动释放,分离线程所用的函数为:pthread_detach(pthread_t tid);
形参:tid 要等待的线程id;
retval为线程退出时,传出的值的指针的指针
//编译:gcc thread_basic.c -o test_exe -pthread
#include
#include
#include
#include
#include
#include
#include
#include
#include
void *thread_entry(void* arg)
{
int arg_val = *(int*)arg;
printf("child thread start with para: %d\n", arg_val);
int *p_out = malloc(sizeof(int));//申请的空间用于传出线程的退出值
//static int data_out = 0;
//int *p_out = &data_out;//局部静态空间也可用来传出退出值
*p_out = arg_val + 1;
pthread_exit(p_out); //在入口函数中,本行代码等价于 return p_out;
return p_out;//这一行实际上执行不到,线程已经被pthread_exit给退出了
}
int main()
{
int status;//线程操作函数的返回值
pthread_t tid;//子线程ID
int arg = 2;//传给子线程入口函数的实参
int *p_recv_out;//pthread_join接收子线程的返回值
//创建子线程
status = pthread_create(&tid, NULL, thread_entry, &arg);
if(0 == status)
{
printf("creat thread ok, tid = %ld\n", tid);
}
else
{
printf("creat thread failed, error info: %s\n", strerror(errno));
return -1;
}
//等待子线程结束,并接收子线程的结束值
status = pthread_join(tid, (void**)&p_recv_out);
if(0 == status)
{
printf("child thread quit normally\n");
printf("parent thread received exit data: %d\n", *p_recv_out);
free(p_rec
运行结果: