线程的创建和终止

一. 线程的创建

#include <pthread.h>
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 <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>

//定义线程运行函数
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 <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

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. 线程终止方式

  • 主动终止:线程的执行函数中调用return语句或者调用pthread_exit()函数;
  • 被动终止:线程可以被同一进程的其他线程取消,其他线程调用pthread_cancel(pthid);
#include <pthread.h>
int pthread_cancel(pthread_t pid);
void pthread_exit(void *retval);
int pthread_join(pthread_t th, void **thread_return);
//返回值:成功返回0,否则返回错误编号;
函数解释:
pthread_cancel:线程可以被同一进程的其他线程取消,tid为被终止的线程标识符;
pthread_exit:1)retval:pthread_exit调用者线程的返回值,可由其他函数和pthread_join来检测获取;
                       2)线程退出时使用函数pthread_exit,是线程的主动行为;
                       3)由一个线程中的多个线程共享数据段,因此通常在线程退出后,退出线程所占用的资源并不会随线程的结束而释放。所有需                             要pthread_join函数来等待线程结束,类似于wait系统调用;
pthread_join:1)th:被等待线程的标识符;
                       2)thread_return:用户定义指针,用来存储被等待线程的返回值;

补充:pthread_join函数的作用?
pthread_join使一个线程等待另一个线程结束。
代码中如果没有pthread_join,主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待,知道等待的线程结束自己才结束,使创建的线程有机会执行。

下面给出一个简单示例:
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

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;
}
该示例就是一个简单的加法,不再作详细的解释了。
在上面线程创建的第二个程序中,在线程运行函数中分别有return和pthread_exit主动退出的说明。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值