共计 2944 个字符,预计需要花费 8 分钟才能阅读完成。
多线程之间同步是继多线程学习之后,须要重点把握的又一个重要内容。一个实时操作系统外面,如果只有多线程而没有线程间同步,各个线程都无序运行,那么必然会导致整个零碎的运行呈现各种问题。
正是因为一个较大的工作拆分为多个小工作,这些小工作是由多个线程去执行的,那么,这些小工作之间必然会存在着千头万绪的关系,小工作的运行更不能只管自扫门前雪,不论别人瓦上霜,因而,线程间同步是必须把握的内容。
对于多线程之间的同步,RT-Thread 提供了比拟丰盛的文档作为参考,具体能够查看以下链接:https://www.rt-thread.org/doc…
本文尝试从以下几个方面总结一下 RT-Thread 线程间同步的学习过程
什么是线程间同步,为什么须要线程同步?
上一篇文章 RT-Thread 学习笔记 —(4)RT-Thread 多线程学习过程总结外面提及到,音乐播放器通过多线程工作的时候,须要通过正当的调度形式,能力让各个线程协同工作。而这里所说的“正当的调度形式”,其中一种形式就是指多线程同步。
什么是线程间同步?艰深一点来说,线程间同步是指多个线程之间进行协商工作的形式。后面曾经说过,线程在工作的时候,尽管只分心在做一件事件,但线程在工作的时候,并不能只埋头苦干,而不顾其余线程的状态,因而必须要有一种形式,来告知其余线程对于本身的工作状态。
为什么须要线程同步?持续用音乐播放器来举例说明,依照前文的图例,如果不应用多线程播放音乐,会有什么结果呢?结果有可能是,音乐文件读取线程比歌词文件读取线程跑得慢,导致歌曲还没播放到那一步,而歌词反而先显示进去了,还有其余可能性,导致歌曲,歌词,MV 三者播放的程序乱套了,不能同步显示。
如果要让音乐播放能失常工作,就须要在 4 个工作线程之间退出线程同步机制。比方歌词文件读取线程可能运行得比拟快,而音乐文件读取线程读取音乐比较慢,那么,这两个线程之间就须要进行同步,快的线程要略微等一下,等慢的线程发送一个同步音讯,这样两者能力一起欢快地持续运行。
线程间同步的形式
针对 RT-Thread 实时操作系统,线程间同步次要有三种形式:信号量,互斥量,事件集。这三种线程同步机制各有优缺点,在理论开发工作外面,须要依据不同的利用场景进行辨别应用。
信号量是一种非常灵活的线程同步形式,通过信号量能够衍生出多种性能,比方,锁、线程同步、资源计数,前面讲述的互斥量也能够通过二值型信号量来实现。生存中的停车场利用场景,就是信号量的一种具体体现。
1. 当停车场空的时候,停车场的管理员发现有很多空车位,此时会让里面的车陆续进入停车场取得停车位;
2. 当停车场的车位满的时候,管理员发现曾经没有空车位,将禁止里面的车进入停车场,车辆在外排队等待;
3. 当停车场内有车来到时,管理员发现有空的车位让出,容许里面的车进入停车场;待空车位填满后,又禁止内部车辆进入。
在这个场景外面,管理员就相当于信号量,管理员手中空车位的个数就是信号量的值(非正数,动态变化);停车位相当于公共资源(临界区),车辆相当于线程。车辆通过取得管理员的容许获得停车位,就相似于线程通过取得信号量拜访公共资源。
信号量是没有“所有权”这种概念的,也就是说,对于一个非二值型的信号量,多个线程能够对其进行获取 / 开释操作,也能够递归获取信号量,因而,对于非二值型信号量,在应用过程中可能会呈现线程优先级翻转和线程死锁的问题。
零碎内核提供以下信号量的 API 函数接口,如下图所示。
互斥量,顾名思义,就是一种互相排挤的信号量,是一种非凡模式的二值型信号量。这种状况就相似于一个停车位,当 A 汽车占据了停车位(获取到互斥量)的时候,其余汽车就不能获取该车位了,必须等 A 汽车来到该车位的时候,能力有机会抢夺该车位。
互斥量跟信号量不同的是,互斥量只有两种状态值,对于领有互斥量的线程,示意该线程曾经领有了该互斥量的所有权和控制权,该互斥量只能由该线程来进行开释,这样就能够解决线程递归获取互斥量呈现的死锁问题。
对于信号量中存在的优先级翻转问题,在互斥量外面不会呈现,这是因为互斥量外面实现了高优先级继承算法。优先级继承是通过在高优先级线程尝试获取共享资源而被挂起的期间内,将低优先级的线程的优先级晋升到高优先级线程的优先级别,从而解决优先级翻转引起的问题。
零碎内核提供以下互斥量的 API 函数接口,如下图所示。
事件集也是线程同步的一种机制,但与信号量或互斥锁不同,事件集是能够实现一对多或多对多同步的。也就是说,一个线程收回一个(或多个)事件,一个(或多个)期待该事件的线程在获取到该事件后,就能够取得运行的权限。
事件集是应用一个 32 位无符号整型的变量来示意的,每一个位示意一个事件,这些事件能够是“逻辑与”或“逻辑或”的关系。换句话说,就是能够让一个线程期待一个事件(逻辑或)达到就执行,或者让一个线程期待所有事件(逻辑与)达到才执行。
事件集在某些场合外面是能够代替信号量的。但事件集与信号量不同,事件集的事件在革除之前,是不能累计的,也就是说,一个线程发送了屡次同一事件,因为不能累计,也就相当于只发了一次该事件,直到该事件被革除。
零碎内核提供以下事件集的 API 函数接口,如下图所示。
多线程同步的利用示例
多线程同步的利用示例,次要是为了验证信号量,互斥量,事件集的 API 接口函数,并且通过试验景象察看这三种线程同步形式的运行状况。
示例源码下载链接:https://github.com/embediot/r…
信号量示例次要演示了一个“生产者 - 消费者”的设计模式,生产者线程一直生产产品(数值加 1)放入仓库(循环数组),消费者线程一直从仓库外面取出产品,仓库的读写操作都须要应用信号量的锁机制进行同步。
互斥量示例次要创立了三个动静线程,这三个动静线程一直抢夺这个互斥量的使用权,通过试验景象能够察看到,持有互斥锁的线程的优先级,会通过优先级继承算法,调整到期待线程优先级中的最高优先级。
事件集示例次要初始化了一个事件集和两个线程,一个线程发送事件,另一个线程期待事件。期待事件的线程,别离应用了“逻辑与”和“逻辑或”这两种事件接管形式。
具体示例的实现能够查看工程源码,在 synchronize.h 头文件中,关上相应的宏定义开关,从新编译工程并下载到开发板即可。
线程间同步的注意事项
在进行多线程间同步的时候,对于信号量,互斥量,事件集这三种线程同步形式,有以下一些注意事项:
1. 中断与线程间的互斥,不能采纳信号量(锁)的形式,应该采纳开关中断的形式。
2. 资源计数类型的利用场景,少数都是混合形式的线程间同步,因为单个的资源解决存在线程的多重拜访,因而须要对资源进行锁形式的互斥操作。
3. 在应用信号量的时候,应该要留神优先级翻转的问题,合理安排工作的优先级。
4. 不能递归获取信号量,否则有可能会造成“死锁”的状况。
5. 线程不能长时间占用互斥量,在取得互斥量之后,不能再更改持有互斥量线程的优先级。
6. 不能在中断服务程序外面应用互斥量。
7. 事件集仅能用于线程同步,不能用于线程间传输数据。
8. 事件集不会造成队列,在革除事件之前,发送屡次跟发送一次都是同样的成果。
原文链接 /;https://club.rt-thread.org/as…