一. 线程的创建
#include
int pthread_create(pthread_t *restrict tidp,
const pthread_attr_t *restrict attr,
void *(*start rtn)(void*), void *restrict arg);
返回:成功返回0,否则返回错误编号
参数:tidp:线程标识符指针;(存放所创建线程的标识符的地址)
attr:线程属性指针;
start_rtn:线程运行函数的起始地址;
arg:传递给线程运行函数的参数;
注:不能保证新线程和调用线程的执行顺序(执行顺序由调度算法决定);
下面给出一个具体的案例来说明线程的创建过程。
#include
#include
#include
#include
#include
//定义线程运行函数
void* th_fn(void *arg)
{
int distance = (int)arg;
int i;
for(i = 1; i <= distance; ++i)
{
printf("%lx run %d\n", pthread_self(), i);
int time = (int)(drand48() * 100000);// 随机睡眠一定的时间
usleep(time); // 微妙
}
// return (void*)0;
return (void*)distance;
}
int main(void)
{
int err;
pthread_t rabbit, turtle; // 定义线程标识符
// 创建rabbit线程
// 第二个参数是线程的属性
// 第三个参数是线程运行函数的起始地址
if((err = pthread_create(&rabbit, NULL,
th_fn, (void*)50)) != 0)
{
perror("pthread_create error");
}
//创建turtle线程
if((err == pthread_create(&turtle, NULL,
th_fn, (void*)50)) != 0)
{
perror("pthread_create error");
}
//主控线程调用pthread_join(),自己会阻塞
//直到rabbit线程和turtle线程结束方可运行
// pthread_join(rabbit, NULL);
// pthread_join(turtle, NULL);
//sleep(10); // 主控线程运a行
int result;
pthread_join(rabbit, (void*)&result);
printf("rabbit race distance is: %d\n", result);
pthread_join(turtle, (void*)&result);
printf("turtle race distance is: %d\n", result);
printf("race is finished\n");
printf("control thread id: %lx\n", pthread_self());
printf("finished\n");
return 0;
}
该案例是一个简易的龟兔赛跑模型。
首先创建两个线程rabbit和turtle。在pthread_create中,第四个参数时传递给线程运行函数得参数。第三个参数是线程运行函数th_fn,这里我们打印出二者所跑的路程,每跑一步都进行一次睡眠。
说明:a. 在程序中,主控线程调用pthread_join()函数后自己会阻塞,rabbit线程和turtle线程运行结束主控线程方可运行;
b. pthread_join函数中的第二个参数存储的是线程运行函数的返回结果;
当然,如果在线程运行函数中我们想要输出更多的内容,也就是参数中包含更多的内容,我们可以将这些内容封装在一个结构体变量中,然后传递给函数,下面的例子对上例做了稍微的改变。
#include
#include
#include
#include
typedef struct
{
char name[20]; //存储线程的名字
int time; //线程睡眠时间
int start;
int end;
}RaceArg;
void* th_fn(void *arg)
{
RaceArg *r = (RaceArg*)arg;
int i = r->start;
for(; i <= r->end; ++i)
{
printf("%s(%lx) running %d\n",
r->name, pthread_self(), i);
usleep(r->time);
}
//return (void*)0;//主动终止
//pthread_exit((void*)0);
return (void*)(r->end - r->start);
}
int main(void)
{
int err;
pthread_t rabbit, turtle;
RaceArg r_a = {"rabbit", (int)(drand48()*100000000), 20, 50};
RaceArg t_a = {"turtle", (int)(drand48()*100000000), 10, 60};
if((err = pthread_create(&rabbit, NULL,
th_fn, (void*)&r_a)) != 0)
{
perror("pthread_create error");
}
if((err = pthread_create(&turtle, NULL,
th_fn, (void*)&t_a)) != 0)
{
perror("pthread_create error");
}
//主控线程调用pthrea_join,自己阻塞
//等待其他线程运行结束自己再运行
// pthread_join(rabbit, NULL);
// pthread_join(turtle, NULL);
int result;
pthread_join(rabbit, (void*)&result);
printf("rabbit race distance is %d\n", result);
pthread_join(turtle, (void*)&result);
printf("turtle race distance is %d\n", result);
printf("race finished\n");
printf("control thread id: %lx\n", pthread_self());
printf("finished.\n");
return 0;
}
在该例子中,我们将线程的名字、每一步的睡眠时间、起始和终止点都封装在了一个结构体变量中,在线程运行函数中,我们又将该结构体变量作为参数传递给线程运行函数,并将其输出,最后线程运行函数返回的是总的路程。
注意:在pthread_create中,第四个参数就不再是一个数了,而是一个包含很多内容的结构体变量。同样,这里需要主控线程进行阻塞。
问题:那么在上述的案例中,两个线程之间会公用一些资源,这期间会不会互相干扰?
我们知道,每个线程都有自己独立的栈空间,像线程运行函数中的一些局部变量就存储在这些空间中。当然不同的线程也有一些共享的资源,比如上图中的数据段部分,该空间存储着全局变量和静态变量,由各个线程共享,显然这样是不安全的,因为一个线程对某一个变量做了修改之后,另一个线程访问该变量时是修改后的。因此,在多线程编程中建议尽量使用局部变量。
二. 线程终止
1. 线程终止方式
#include
int pthread_cancel(pthread_t pid);
void pthread_exit(void *retval);
int pthread_join(pthread_t th, void **thread_return);
//返回值:成功返回0,否则返回错误编号;
函数解释:
#include
#include
#include
#include
typedef struct
{
int d1;
int d2;
}Arg;
void* th_fn(void *arg)
{
Arg *r = (Arg*)arg;
//return (void*)(r->d1 + r->d2);]
return (void*)r;
}
int main(void)
{
int err;
pthread_t th;
Arg r = {20, 50};
if((err = pthread_create(&th, NULL,
th_fn, (void*)&r)) != 0)
{
perror("pthread_create error");
}
/*
int *result;
pthread_join(th, (void**)&result);//第二个参数获得子线程的返回结果
printf("result is %d\n", (int)result);//这里做了强制转换
*/
/*
int result;
pthread_join(th, void(*)&result);
printf("result is %d\n", result);//这里就不需要强制转换
*/
int *result;
pthread_join(th, (void**)&result);
printf("result is %d\n",
((Arg*)result)->d1 + ((Arg*)result)->d2);
return 0;
}
该示例就是一个简单的加法,不再作详细的解释了。