线程
-线程的定义
线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,一个进程是可以有多个线程
多个线程共享一个进程的资源,每个线程参与操作系统的统一调度
(可以理解为进程=进程资源+主线程+子线程)
-线程与进程
线程与进程的区别
内存空间
1.一个进程中多个线程共享同一个内存空间
2.多个进程拥有独立的内存空间
进程/线程间通讯
1.线程间通讯方式简单
2.进程间通讯方式复杂
并发操作,线程比进程更节约资源
(联系紧密的任务在并发时优先选择多线程,如果任务之间比较独立,在并发时建议选择多多进程)
-线程资源
共享进程的资源:
1.同一块地址空间
2.文件描述符
3.每种信号的处理方式(如:SIG_DFL,SIG_IGN或者自定义的信号优先级)
4.当前工作目录
5.用户id和组id
独立的资源
1.线程线
2.每个进程都私有的上下文信息
3.线程ID
4.寄存器的值
5.errno的值
6.信号屏蔽字以及调度优先级
-线程相关的命令
pidstat -t -p:查看相应进程的线程
ps -ef | grep:查看进程
ps -T -p:查看某个进程下所有线程
top -H -p:查看某一个进程下的线程
pthread_create:线程创建
注:1.一旦子线程创建成功,则会被独立调度执行,并且与其他线程并发执行
2.在编译时需要链接-ipthread
pthread_exit:线程退出
注:1.当主线程调用pthread_exit函数时,进程不会结束,也不会导致其他子线程退出
2.任何线程调用exit函数会让进程结束
pthread_join:阻塞调用线程
注:1.主线程需要等待子线程退出,并释放子线程资源
pthread_detach:使线程分离
注:线程分为可结合的与可分离的
可结合的线程能够被其他线程收回其资源和杀死;在被其他线程回收之前,它的存储器资源(如栈)是不释放的
线程创建的默认状态为可结合的,可以由其他线程调用pthread_join函数等待子线程退出并释放相关资源
线程分离函数不会阻塞线程的执行
-创建多个线程
创建多个线程时,一般由主线程统一创建,并等待释放资源或者分离线程,不可以递归创建
1.多个线程如果任务相同,则可以使用同一个线程执行函数
2.多个线程如果任务不同,则可以使用不同的线程执行函数
-线程通讯
线程通讯就是当多个线程共同操作共享的资源时,互相告知自己的状态以避免资源争夺
主线程向子线程传递参数:pthread_create函数的第四个参数arg
子线程给主线程传递参数:
1.在子线程将需要返回的值存储在pthread_exit函数中的retval参数中
2.在主线程中通过pthread_join函数的第二个参数retval得到返回值,pthread_join函数会将线程的返回值(指针)保存到retval中
-线程互斥锁
-关于线程互斥锁
线程的主要优势在于能够通过全局变量来共享资源,不过这种便捷的共享是有代价的:
1.必须确保多个线程不会同时修改同一变量
2.某一线程不会读取正由其他线程修改的变量,实际就是不能让俩个线程同时对临界区进行访问
线程互斥锁则可以用于解决多线程资源竞争问题
tips:
atoi函数:将数字字符串转换为整数
-线程互斥锁
线程互斥锁工作机制
当线程A获得锁,另外一个线程B在获得锁是则会阻塞,直到线程A释放锁,线程B才会获得锁
线程互斥锁工作原理
本质上是一个pthread_mutex_t类型的变量,假设名为V
当V=1,表示当前临界资源可以被竞争访问
当V=0,表示当前临界资源正在被某个线程访问,其他线程需要等待
线程互斥锁的特点
1.一个pthread_mutex_t类型的变量,就代表一个互斥锁
2.如果俩个线程访问的是同一个pthread_mutex_t变量,那么他们访问了同一个互斥锁
3.对应的变量定义在pthreaddtypes.h头文件中,是一个共用体包含一个结构体
线程互斥锁的初始化
静态初始化:pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER
动态初始化:
pthread_mutex_init函数:初始化线程互斥锁
pthread_mutex_destroy函数:销毁线程互斥锁
线程互斥锁的操作
线程互斥锁的操作主要分为获取锁与释放锁
pthread_mutex_lock函数:获取锁的函数
pthread_mutex_unlock函数:释放锁的函数
-线程同步
-线程同步的概念
线程同步:是指在互斥的基础上,通过其他机制实现访问者对资源的有序访问
条件变量:线程库提供专门针对线程同步的机制
线程同步比较典型的应用场合就是生产者和消费者
-生产者与消费者模型原理
在这个模型中,包括生产者线程和消费者线程,通过线程来模拟多个线程同步的过程
这个模型中,需要以下组件:
1.仓库:用于存储产品,一般作为共享资源
2.生产者线程:用于生产产品
3.消费者线程:用于消费产品
原理:
1.当仓库没有产品时,则消费者线程需要等待,直到有产品时才能消费
2.当仓库已经装满产品时,则生产者线程需要等待,直到消费者线程消费产品之后才会生产(轮询是否有产品可以消费,非常消耗CPU资源)
-条件变量
条件变量:允许一个线程就某个共享变量的状态通知其他线程,并让其他线程等待这一通知
-条件变量的初始化
静态初始化: pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
动态初始化:
pthread_cond_init函数:初始化条件变量
pthread_cond_destory函数:销毁条件变量
-条件变量原理
step 1 :
消费者线程判断消费条件是否满足(仓库是否有产品),如果有产品则可以消费,然后解锁
step 2 :
当条件满足仓库产品数量为0时,调用pthread_cond_wait函数,这个函数做的事情如下:
1.在线程睡眠之前,对互斥锁解锁
2.让线程进入到睡眠状态
3.等待条件变量收到信号时,该函数重新竞争锁,并获取锁
step 3 :
重新判断条件是否满足,如果满足,则继续调用pthread_cond_wait函数
step 4 :
唤醒后,从pthread_cond_wait返回,条件不满足,则正常消费产品
step 5 :
释放锁,整个过程结束
pthread_cond_wait函数:阻塞线程,等待唤醒
tips:
1.条件变量需要与互斥锁结合使用,先获得锁才能进行条件变量的操作
2.调用函数后会释放锁,并阻塞线程
3.一旦线程唤醒,需要重新竞争锁,重新获得锁之后,pthread_cond_wait函数返回
pthead_cond_signal函数:唤醒所有阻塞在某个条件变量上的线程(主要适用等待线程都在执行完全相同的任务)
pthread_cond_broadcast函数:唤醒所有阻塞在某个条件变量上的线程(主要适用等待线程都执行不相同的任务)
(条件变量并不保存状态信息,条件变量代表时一个通讯机制)
-问题
1. 为什么条件变量需要与互斥锁结合起来使用?
答:防止在调用 pthread_cond_wait 函数等待一个条件变量收到唤醒信号,另外一个线程发送信号在一个线程实际等待它之前,也就是说线程还没有完全进入到睡眠状态,其他线程发送唤醒信号
2.在判断条件时,为什么需要使用 while(number == 0),而不是 if()?
答:防止虚假唤醒
能够唤醒的情况如下:
1.被信号唤醒,并非由条件满足而唤醒
2.条件变量状态时,一次唤醒多个线程,但是被其他线程先消费玩产品,等到当前线程执行时,条件已经不满足