RT-Thread学习笔记六——线程间通讯(信号量的使用)

目录

1.概念

1.1  二值型信号量

1.2  计数型信号量

2.信号量的创建(API) 

2.1动态信号量创建

2.2动态信号量的删除

2.3静态信号量的创建 

2.4静态信号量的删除

3.信号量的获取

4.信号量的释放

5.实际使用


嵌入式实时性操作系统中,运行的主要为线程和ISR(中断服务程序),运行步骤有时需要同步处理(相互之间交换数据),有时需要互斥处理(同一时刻只有一个线程访问公共资源),为满足需求,操作系统提供了统称为线程间通讯的功能。

线程间通讯包括:信号量,互斥量,事件,邮箱,消息队列。本文讲述信号量的基本概念以及其具体的代码使用。

1.概念

线程通过信号量来的控制来访问公共资源,信号量的值就是未被线程使用的公共资源的个数,每个信号量对象都有一个对应的信号量值和线程等待队列,线程等待队列为未成功获取信号量的线程。

信号量分为计数信号量和二值信号量。

1.1  二值型信号量

二值信号量只有0和1,类似于一个标志位,相比于使用一个全局变量作为标志位,二值信号量可以更好的利用CPU资源,避免浪费,当线程信号量为0时,申请信号量线程进入阻塞状态,信号量为1时,唤醒该线程。

1.2  计数型信号量

计数型型号了数值范围为0-65535,可以允许多个线程访问公共资源,但会限制访问资源的最大数目,当达到最大数目,后续申请资源的线程会进入阻塞状态,直到有线程释放了信号量。

  • 事件计数:事件产生时释放信号量,事件处理时获取信号量,当值为0表示所有事件处理完毕
  • 资源管理:信号量值代表当前资源可以数量,值为0说明没有资源了(比如停车场车位)

以上两种会经常用到计数型信号量。

2.信号量的创建(API) 

信号量创建分为动态信号量和静态信号量,类似与线程创建,区别为是否由内核分配空间。

2.1动态信号量创建

动态信号量创建API,

static rt_sem_t dynamic_sem = RT_NULL;
void main(){
dynamic_sem= rt_sem_create	(	const char * 	name,
                                         rt_uint32_t 	value,
                                          rt_uint8_t 	flag 
                                               )	
}

首先创建一个指向信号量的指针dynamic_sem,然后将信号量创建函数返回参数赋予dynamic_sem,信号量创建函数rt_sem_create参数为信号量名称,信号量数值,以及信号量标志位,创建错误时返回RT_NULL。

RT-Thread学习笔记六——线程间通讯(信号量的使用)_第1张图片

信号量标志位决定了线程等待队列排队的方式,标志位有两个值

  • RT_IPC_FLAG_FIFO,先进先出方式。等待信号量的线程按照先进先出的方式排队,先进入的线程将先获得等待的信号量。
  • RT_IPC_FLAG_PRIO,优先级等待方式。等待信号量的线程按照优先级进行排队,优先级高的等待线程将先获得等待的信号量。

2.2动态信号量的删除

动态信号量删除API

static rt_sem_t dynamic_sem = RT_NULL;
dynamic_sem= rt_sem_create	(	const char * 	name,
                                         rt_uint32_t 	value,
                                          rt_uint8_t 	flag 
                                               )	
 
rt_sem_delete	(dynamic_sem)	;  //删除信号量dynamic_sem

RT-Thread学习笔记六——线程间通讯(信号量的使用)_第2张图片 参数为信号量 对象句柄,即创建的指向信号量的指针。本文中为dynamic_sem

2.3静态信号量的创建 

静态信号量创建API

静态信号量的创建需要先定义一个结构体,类型为struct rt_semaphore

rt_err_t rt_sem_init	(	rt_sem_t 	sem,
const char * 	name,
rt_uint32_t 	value,
rt_uint8_t 	flag 
)	

RT-Thread学习笔记六——线程间通讯(信号量的使用)_第3张图片

参数中sem为指向刚刚创建的类型为struct rt_semaphore的结构体的指针,其他和动态创建信号量相同。

2.4静态信号量的删除

静态信号量的删除API

rt_sem_detach	(	rt_sem_t 	sem	)	

RT-Thread学习笔记六——线程间通讯(信号量的使用)_第4张图片  

删除函数参数sem为信号量句柄,即指向信号量的指针,本文中为dynamic_sem

3.信号量的获取

信号量获取API

static rt_err_t result ;  
result=rt_sem_take (rt_sem_t sem, rt_int32_t time);

首先创建结构体变量result,将信号量获取函数返回值赋予result,若result为RT-EOK,则成功获取信号量。

参数说明:

RT-Thread学习笔记六——线程间通讯(信号量的使用)_第5张图片

线程通过调用该函数获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号量, 并且相应的信号量值会减 1;如果信号量的值等于零,那么说明当前信号量资源实例不可用,申请 该信号量的线程将根据 time 参数的情况选择直接返回、或挂起等待一段时间、或永久等待,直到其他线程或中断释放该信号量。

解释:sem为信号量对应指针,本文中为dynamic_sem,参数time为等待时间,根据设定不同的等待时间,线程会执行三种不同的操作:

  • 参数值为零,则函数会直接返回。
  • 参数值不为零,则会等待设定的时间。
  • 参数值为最大时钟节拍数(RT_WAITING_FOREVER),则会永久等待,直到其他线程或中断释放该信号量

 无等待信号量获取API

rt_sem_trytake	(	rt_sem_t 	sem	)	

参数sem为信号量指针句柄

RT-Thread学习笔记六——线程间通讯(信号量的使用)_第6张图片

 当使用此函数时,线程不再等待,如果线程无法获取信号量,则直接返回错误码,反之则获取成功。

4.信号量的释放

信号量释放API

rt_sem_release	(	rt_sem_t 	sem	)	

RT-Thread学习笔记六——线程间通讯(信号量的使用)_第7张图片

 该函数将释放一个信号量,当信号量的值等于零时,并且有线程等待这个信号量时, 释放信号量将唤醒等待在该信号量线程队列中的第一个线程,由它获取信号量;否则 将把信号量的值加一。

5.实际使用

#include 
static rt_sem_t dynamic_sem = RT_NULL;  //定义线程指针
ALIGN(RT_ALIGN_SIZE)    //指针格式对齐


void count(void *parameter)
{
 static  int value=0;
 while(value<10){
         value++;
         rt_thread_mdelay(1500);     //每隔1.5s释放一次信号量
         rt_kprintf("count release xinhao\n");
         rt_sem_release(dynamic_sem);   //信号量释放
    }
}

ALIGN(RT_ALIGN_SIZE)
void result(void *parameter)
{
    static rt_err_t getxinhao ;  //信号量获取返回值
    static int number=0;
    while(1){             //循环打印
    getxinhao=rt_sem_take (dynamic_sem, RT_WAITING_FOREVER);  //信号量获取
    if (getxinhao==RT_EOK) {            //获取到信号量则加1
        number++;
        rt_kprintf("number=%d\n",number);
    }
     else {       //获取信号量失败则打印失败
        rt_kprintf("xinhao get failed");
        rt_sem_delete(dynamic_sem);
        return;
    }
    }
}

int main(){

        dynamic_sem= rt_sem_create  ("xinhao1",0,RT_IPC_FLAG_FIFO ) ;  //创建信号量xinhao1,最大访问数目1,线程等待队列为先进先出模式
        if (dynamic_sem!= RT_NULL)
        {
        rt_kprintf("xinhao create done");        //信号量创建成功标志
        }
        else {
        rt_kprintf("xinhao create failed");
         }

  static rt_thread_t count2 = RT_NULL ;
  count2= rt_thread_create("count",    //线程名称
                                        count,  //线程入口函数
                                        RT_NULL,        //参数
                                        1024,           //线程栈
                                        25,             //优先级
                                        5);    //时间片

          if (count2 != RT_NULL)
          {
               rt_thread_startup(count2);     //线程创建成功则开启线程
          }
          else{
             rt_kprintf ("count failed\n") ;
          }

  static rt_thread_t result2 = RT_NULL ;
  result2= rt_thread_create("result",    //线程名称
                                         result,  //线程入口函数
                                         RT_NULL,        //参数`
                                         1024,           //线程栈
                                         24,             //优先级
                                         5);            //时间片
           if (result2 != RT_NULL)
           {
                rt_thread_startup(result2);  //线程创建成功则开启线程
           }
           else{
              rt_kprintf ("result failed\n") ;
           }
return 0;
}


/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(main, task1);

参考官方示例编写,官方为创建两个静态线程和一个动态信号量,由于实际使用中动态线程使用较多,做了一些修改,实际烧录后效果与官方例程相同

每隔1.5s释放一次信号量,串口输出一次number数值,间隔输出count  release xinhao,共十次

RT-Thread学习笔记六——线程间通讯(信号量的使用)_第8张图片

你可能感兴趣的:(RT-Thread,Studio,STM32单片机学习,学习)