前两篇文章总结了RT-Thread多线程以及多线程同步的学习过程,关于前两篇学习总结,能够查看以前的文章。html
本篇文章继续总结关于RT-Thread多线程相关的最后一个重要知识点:线程间通讯。前面的文章屡次说起到,一个大的任务拆分为多个小任务,这些小任务之间必然存在着各类各样的关系,致使这些小任务的线程不能各自为政,必需要考虑其余任务线程的运行状况。git
既然已经有了线程间同步,可让多个线程之间进行相互沟通,那为啥还须要线程间通讯呢?线程间通讯究竟是什么东西,这种方式有什么应用场景?github
关于多线程之间的通讯,RT-Thread官方提供了比较丰富的文档做为参考,具体能够查看如下连接:https://www.rt-thread.org/doc...多线程
本文尝试从如下几个方面总结一下RT-Thread线程间通讯的学习过程
线程间通讯的相关概念异步
什么是线程间通讯?通讯,顾名思义,就是双方须要进行沟通与对话。通俗地归纳,就是A线程在工做运行期间,有某些数据或者信息,要告诉B线程,让B线程接收到这些数据或信息后,可以继续完成指定的任务和工做。函数
两个线程之间为何要进行通讯呢?仍是那句话,多个任务线程并非独立的,它们在工做的时候是须要根据业务场景进行必定的沟通的,仍是以音乐播放器举例,当歌词读取线程把歌词从硬盘里面读出来了,要把这一串读到的歌词告诉给显示线程,让它把歌词显示出来。这个“告诉”的动做,就是经过线程间通讯来进行的。
2.png
既然都是为了协调线程的工做状态,线程间同步和线程间通讯这二者有什么区别呢?区别就是线程间同步能作的事情太有限了,线程间同步只是告诉一下对方“别跑太快,等等我嘛~”,而线程间通讯,就是有一大堆的数据和信息要告知对方,万一A线程有不少话要跟B线程说,线程同步这种方式就不能知足要求了,因此须要线程间通讯。
线程间通讯的方式学习
针对RT-Thread实时操做系统,线程间通讯主要有三种方式:邮箱,消息队列,信号。这三种线程间通讯机制都有各自的特色,在实际开发工做里面,须要根据不一样的应用场景进行区分使用。url
邮箱是线程间通讯的其中一种方式,这个邮箱的概念,跟咱们生活中使用的邮箱概念,实际上是大同小异的,在生活中,若是咱们有信件要寄,就把信件往邮筒一扔就能够了,邮局会负责把信件送往目的地。操作系统
一样的道理,当A线程有信件(即数据)要发送给B线程,只须要调用操做系统提供的邮箱相关接口函数,把数据发送出去,操做系统就会负责把数据转发到目标线程,整个转发过程是怎样实现的,收和发的线程都不须要关心。.net
使用邮箱进行线程间通讯,特色是开销低,效率高。这是由于,每一个邮件信息最多只能是4个字节的内容,因此,这个邮件信息能够是某个数据块的指针,经过指针传递的方式,来传输更多的数据。
邮箱在使用过程当中,可能会存在邮箱空或邮箱满的状况,在邮箱空的状况下,接收邮件的线程会选择挂起等待,或者等超时时间到来。在邮箱满的状况下,发送邮件的线程会选择挂起或直接返回一个邮箱满的返回值。
系统内核提供如下邮箱相关的API函数接口,以下图所示。
消息队列是另一种比较经常使用的线程间通讯方式,至关于邮箱的扩展。跟邮箱不一样的是,消息队列是能够接收不定长的数据的,而且把这个不定长的数据复制到自身线程的内存空间。
消息队列其实就是一个数据存储空间,这个存储空间遵循先进先出的原则,也就是说,不论是什么消息,等待消息的线程得到的是最早进入队列的消息。
消息队列控制块里面,其实有两个链表,一个链表是用来挂接空的消息块(也就是没有内容的消息队列),另外一个链表是用来挂接存有消息的消息块,具体抽象以下图所示。
当线程A要发送一个消息时,先从空闲消息块链表取出一个块空间,把消息装进去后,把这个消息块挂接到非空消息块链表的队尾。若是使用紧急方式发送消息,则把该消息块挂接到非空消息链表的队首。线程获取消息的时候,老是会获取链表头的消息的。
系统内核提供如下消息队列相关的API函数接口,以下图所示。
信号,在软件层次上其实至关于一种软中断的方式,这种中断机制是操做系统模拟出来的,一个线程收到一个信号,跟硬件处理器收到一个硬件中断请求,这个过程基本上是相似的。
当一个线程在正常运行期间,若是其余线程有突发的事件或异常通知须要处理,就能够经过信号的方式发送出去,线程在正常运行期间不须要等待信号的到来(由于不知道信号何时会到来)。
收到信号的线程,对各类信号的处理有如下三种方法:
一、相似中断的处理程序,能够针对须要处理的信号指定处理函数,由该函数来处理。
二、直接忽略某个信号,对该信号不作任何处理,就像未发生过同样。
三、使用系统保留的默认值来处理该信号。
系统内核提供如下信号相关的API函数接口,以下图所示。
多线程通讯的应用示例
多线程通讯的应用示例,主要是为了验证邮箱,消息队列,信号的API接口函数,而且经过实验现象观察这三种线程通讯方式的运行状况。
示例源码下载连接:https://github.com/embediot/r...
邮箱示例主要是初始化了2个静态线程,一个静态的邮箱对象,线程 2 发送邮件,共发送 11 次,线程 1 接收邮件,共接收到 11 封邮件,将邮件内容打印出来,并判断结束。
消息队列示例主要初始化了2个静态线程,线程 1 会从消息队列中收取消息,线程 2 定时给消息队列发送普通消息和紧急消息。因为线程 2 发送消息 “I” 是紧急消息,会直接插入消息队列的队首,因此线程 1 在接收到消息 “B” 后,接收的是该紧急消息,以后才接收消息“C”。
信号示例主要是建立了 1 个线程,在安装信号时,信号处理方式设为自定义处理,定义的信号的处理函数为 thread1_signal_handler(),待此线程运行起来安装好信号以后,给此线程发送信号,此线程将接收到信号,并打印信息。
具体示例的实现能够查看工程源码,在thread_communication.h头文件中,打开相应的宏定义开关,从新编译工程并下载到开发板便可。
线程间通讯的注意事项
在进行多线程间通讯的时候,关于邮箱、消息队列、信号这三种线程间通讯方式,有如下一些注意事项:
1.使用邮箱进行线程间通讯时,因为一封邮件最多只能是4个字节长度,所以若是要传递较多数据信息,可使用结构体进行信息封装,经过指针方式进行传递。
2.邮件发送是非阻塞的,所以能够应用于中断服务程序中。但邮件接收是阻塞的,能够设置接收超时的时间,不能在中断服务程序里面使用邮件接收。
3.当邮箱没有邮件且超时时间不为0 ,邮件的接收过程自动变为阻塞方式。当邮箱满了后,发送线程能够选择挂起等待或直接返回邮箱满的错误码。
4.消息队列是一种异步的通讯方式,消息队列里面的消息老是遵循先进先出的原则。
5.能够在线程或中断服务程序里面能够给消息队列发送消息,但不能在中断服务程序里面接收消息。
6.能够往消息队列里面发送紧急消息,紧急消息会被放置到消息队列的链表头,会首先被等待的线程获取。
7.信号跟信号量不一样,不能混淆二者的概念,信号是软件层面上的一种软中断方式。
8.线程不会用阻塞的方式等待信号的到来,由于线程自身也不知道这个信号(软中断)何时会到。
9.线程对信号的处理,能够设置为捕捉信号,忽略信号,使用默认方式处理信号。