嵌入式Linux开发:线程、多线程

学习视频 <— 众所周知B站是中国最大的学习网站 2333

快速浏览

  • 第一部分:线程的基础知识
      • 1.线程的概念
      • 2.线程的特点
      • 3.进程与线程的关系
      • 4.为什么要用线程
  • 第二部分: Linux线程的实现
      • 1.编译
      • 2.线程标识
      • 3.线程创建(龟兔赛跑例程)
      • 4.线程终止
      • 5.线程清理和控制
      • 6.设置和获得分离属性
  • 第三部分:Linux线程的互斥和同步
      • 互斥锁
      • 读写锁
      • 条件变量
      • 线程信号量
      • 死锁

第一部分:线程的基础知识

1.线程的概念

  • 线程是进程的一个执行单元,是进程内的调度实体
  • 线程按照其调度者可分为用户级线程和内核级线程两种
    • 用户级线程:主要解决的是上下文切换的问题,其调度过程由用户决定。
    • 内核级线程:由内核调度机制实现
  • 现在大多数操作系统都采用用户级线程和内核级线程并存的方法。
  • 用户级线程要绑定内核级线程运行,–个进程中的内核级线程会分配到固定的时间片,用户级线程分配的时间片以内核级线程为准。
  • 默认情况下用户级线程和内核级线程是一对一,也可以多对
    这样实时性就会比较差。
  • 当cpu分配给线程的时间片用完后但线程没有执行完毕,此时线程会从运行状态返回到就绪状态,将cpu让给其它线程使用。

2.线程的特点

  1. 线程是比进程更小的独立运行的基本单位。线程也被称为轻量级进程
  2. 同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间
  3. 进程退出,进程中所有线程全部退出
  4. 一个进程崩溃后,不会对其它进程产生影响;但是一个线程崩溃后,整个进程就崩溃,所以多进程比多线程健壮
  5. 线程不可能完全替代进程
  6. 线程拥有自己独立的属性(线程ID(TID),切换状态,调度优先级,函数栈,错误号,信号屏蔽字和未决信号集,task_ struct结构体)
  7. 线程的开销比较低,实际上是函数的切换
  8. 线程的通信简单:全局变量
  9. linux线程支持 是基于库的,库的优点是可以跨平台

3.进程与线程的关系

线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一用户内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。
一个进程至少需要一个线程作为它的指令执行体,进程管理着资源(比如cpu、内存、文件等等)。而将线程分配到某个cpu上执行。
嵌入式Linux开发:线程、多线程_第1张图片

4.为什么要用线程

因为线程和进程比起来很小,所以相对来说,线程花费更少的CPU资源。
	
在操作系统设计上,从进程演化出线程,最主要的目的就是更好地支持多处理器,并且减小进程上下文切换的开销。

第二部分: Linux线程的实现

1.编译

首先在项目上右击,属性,依次点击
C/C++ Build -> Settings ->右边Tool Settings -> 下面GCC C Linker
在右上侧Command中添加 -lpthread
嵌入式Linux开发:线程、多线程_第2张图片

2.线程标识

每个进程内部的不同线程都有自己的唯一标识(ID)
线程标识只在它所属的进程环境中有效
线程标识是 pthread_t 数据类型
#include 

int pthread equal(pthread_t, pthread_t);
//返回:相等返回非0,否则返回O

pthread_t pthread_self(void);
//返回:调用线程的线程ID

3.线程创建(龟兔赛跑例程)

tidp: 线程标识符指针
attr: 线程属性指针
start_rtn:线程运行函数的起始地址
arg:传递给线程运行函数的参数
		
新创建线程从 start_rtn 函数的地址开始运行
不能保证新线程和调用线程的执行顺序
#include 
int pthread_create(pthread t *restrict tidp,
	const pthread_attr_t *restrict attr,
	void *(*start_rtn)(void*), void *restrict arg);
//返回:成功返回0,否则返回错误编号
//创建线程示例--->龟兔赛跑(初级版)

#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()*10000);
		usleep(time);//微秒
	}

	return (void*)0;
}

int main(void)
{

	int err;//存储错误码

	pthread_t rabbit,turtle;//定义兔子/乌龟线程

	//创建rabbit线程

	if((err=pthread_create(&rabbit,NULL,
			th_fn,(void *)50))==-1){
		perror("pthread_creat error");
	}

	//创建turtle线程

	if((err=pthread_create(&turtle,NULL,
			th_fn,(void *)50))==-1){
		perror("pthread_creat error");
	}

	//主控线程调用pthread_join(),自己会阻塞
	//直到rabbit和turtle线程结束方可运行
	pthread_join(rabbit,NULL);
	pthread_join(turtle,NULL);
	//sleep(10);
	printf("主控线程的ID:%lx\n",pthread_self());
	printf("结束\n");
	return 0;
}
//创建线程示例--->龟兔赛跑(升级版)

#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;
}

int main(void){

	int err;
	pthread_t rabbit,turtle;

	RaceArg r_a={"rabbit",(int)(drand48()*10000000),2,16};
	RaceArg t_a={"turtle",(int)(drand48()*1000),1,15};

	if((err=pthread_create(&rabbit,NULL,
			th_fn,(void *)&r_a))==-1){
		perror("pthread_create error");
	}

	if((err=pthread_create(&turtle,NULL,
			th_fn,(void *)&t_a))==-1){
		perror("pthread_create error");
	}

	pthread_join(rabbit,NULL);
	pthread_join(turtle,NULL);
	//sleep(5);
	printf("主控线程ID:%lx\n",pthread_self());
	printf("finish");

	return 0;
}

嵌入式Linux开发:线程、多线程_第3张图片

4.线程终止

  • 主动终止
    1.线程的执行函数中调用return语句
    2.调用pthread_exit()
  • 被动终止
    线程可以被同一进程的其他线程取消,其它线程调pthread_cancel(pthid)
#include 

int pthread_cancel(pthread_ t tid);
void pthread_exit(void *retval);
int pthread_join(pthread_t th, void **thread_return);//特殊

返回值:成功返回0,否则返回错误编号
  1. pthread_cancel
    线程可以被同-进程的其他线程取消,tid为被终止的线程标识符。

  2. pthread_exit
    retval: pthread_exit调用者线程的返回值,可由其他函数和pthread_join来 检测获取。
    线程退出时使用函数pthread_exit是线程的主动行为。
    由于一个进程中的多个线程共享数据段,因此通常在线程退出后,
    退出线程所占用的资源并不会随线程结束而释放。
    所有线程想结束后马上释放资源,就需要在主控线程中调用pthread_join函数来等待线程结束,类似于wait系统调用。

  3. pthread_join
    th:被等待线程的标识符
    thread_return:用户定义指针,用来存储被等待线程的返回值
    调用此方法的线程会被阻塞

//pthread_join等待线程结束并返回主线程值示例

#include
#include
#include

typedef struct{
	int d1,d2;
}Arg;

void *th_fn(void *arg){

	Arg *r=(Arg *)arg;

	return (void *)(long)(r->d1+r->d2);
}

int main(void){

	int err;
	int *result;
	int result2;

	pthread_t th;

	Arg r={20,50};

	if((err=pthread_create(&th,NULL,th_fn,(void *)&r))==-1){
		perror("pthread_create error");
	}

	//方法一
	pthread_join(th,(void **)&result);
	printf("result is %d\n",(int)result);

	//方法二
	//pthread_join(th,(void *)&result2);
	//printf("result is %d\n",result2);

	printf("主线程结束");

	return 0;
}

方法一原理图
嵌入式Linux开发:线程、多线程_第4张图片

5.线程清理和控制

#include 
void pthread_cleanup_push(void (*rtn)(void *), void* arg);
void pthread_cleanup_pop(int execute);
返回:成功返回0,否则返回错误编号

这一组函数是成对出现和使用的

  • 参数
    rtn:清理函数指针
    arg:调用清理函数传递的参数
    execute:值1时执行线程清理函数,值0时不执行线程清理函数。
  • 触发线程调用清理函数的动作
    调用pthread_exit
    响应取消请求
    用非零execute参数调用thread_cleanup_pop时
#include
#include
#include

//定义线程清理函数
void clean_fun(void *arg){
	char *s=(char *)arg;
	printf("clean_func: %s\n",s);
}

void *th_fn(void *arg){

	int execute=(int)arg;
	pthread_cleanup_push(clean_fun,"first clean func");
	pthread_cleanup_push(clean_fun,"second clean func");
	printf("thread running %lx\n",pthread_self());
	pthread_cleanup_pop(execute);
	pthread_cleanup_pop(execute);

	return (void *)0;
}

int main(void){

	int err;
	pthread_t	th1,th2;

	if((err=pthread_create(&th1,NULL,th_fn,(void *)1))==-1){
		perror("pthread_creat error");
	}
	pthread_join(th1,NULL);
	printf("th1(%lx) finished\n",th1);


	if((err=pthread_create(&th2,NULL,th_fn,(void *)1))==-1){
		perror("pthread_creat error");
	}
	pthread_join(th2,NULL);
	printf("th2(%lx) finished\n",th2);
	return 0;
}

6.设置和获得分离属性

#include 
int pthread_attr_getdetachstat(const pthread_attr_t *restrict attr,int *detachstate);
int pthread_attr_setdetachstat(const pthread_attr_t *attr,int detachstate);
返回:成功返回0,出错返回错误编号
  • detachstate取值
    PTHREAD CREATE JOINABLE(默认值)正常启动线程
    PTHREAD_ CREATE DETACHED以 分离状态启动线程
  • 以默认方式启动的线程,在线程结束后不会自动释放占有的系统资源,要在主控线程中调用pthread_join()后才会释放。
  • 以分离状态启动的线程,在线程结束后会自动释放所占有的系统资源。
  • 分离属性在网络通讯中使用的较多。

第三部分:Linux线程的互斥和同步

互斥锁

读写锁

条件变量

线程信号量

死锁

你可能感兴趣的:(嵌入式Linux,C/C++)