当前位置: 首页 > news >正文

RT-Thread信号量使用(动态/静态信号量) 及 信号量的四种使用场合

  信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。

  RT-Thread 的信号量有静态和动态,这里同线程的静态和动态是一个意思。对信号量有两种操作,take 和 release。

  程序中,首先初始化信号量为0,这时首先使用take,并只等待10个tick,故一定会超时,因为信号量初始值为0,take不到。然后release一次,信号量便增加1,这时再次take,并且使用的是wait forever 的方式,便一定能得到信号量。就是说,线程如果想持有信号量,但信号量的初始值为“0”,如果想要持有信号量就必须先release一次。

程序:

 

#include <rtthread.h>  
  
static struct rt_semaphore static_sem;  
  
static rt_sem_t dynamic_sem = RT_NULL;  
  
static rt_uint8_t thread1_stack[1024];  
struct rt_thread thread1;  
static void rt_thread_entry1(void *parameter)  
{  
    rt_err_t result;  
    rt_tick_t tick;  
  
    /* static semaphore demo */  
    tick = rt_tick_get();  
  
    /* try to take the sem, wait 10 ticks */  
    result = rt_sem_take(&static_sem, 10);  
  
    if (result == -RT_ETIMEOUT)  
    {  
        if (rt_tick_get() - tick != 10)  
        {  
            rt_sem_detach(&static_sem);  
            return ;  
        }  
        rt_kprintf("take semaphore timeout\n");  
    }     
    else   
    {  
        rt_kprintf("take a static semaphore, failed.\n");  
        rt_sem_detach(&static_sem);  
        return ;  
    }     
  
    /* release the semaphore */       
    rt_sem_release(&static_sem);    
    /* wait the semaphore forever */  
    result = rt_sem_take(&static_sem, RT_WAITING_FOREVER);  
  
    if (result != RT_EOK)  
    {  
        rt_kprintf("take a static semaphore, failed.\n");  
        rt_sem_detach(&static_sem);  
        return ;  
    }  
  
    rt_kprintf("take a static semaphore, done.\n");  
    /* detach the semaphore object */  
    rt_sem_detach(&static_sem);  
//}  
  
/* dynamic thread pointer */  
//static void thread2_entry(void *parameter)  
//{  
//    rt_err_t result;  
//    rt_tick_t tick;  
  
    tick = rt_tick_get();  
  
    /* try to take the semaphore, wait for 10 ticks */  
    result = rt_sem_take(dynamic_sem, 10);  
    if (result == -RT_ETIMEOUT)  
    {  
        if (rt_tick_get() - tick != 10)  
        {  
            rt_sem_delete(dynamic_sem);  
            return ;  
        }          
        rt_kprintf("take semaphore timeout\n");  
    }  
    else  
    {  
        rt_kprintf("take a dynamic semaphore, failed.\n");  
        rt_sem_delete(dynamic_sem);  
        return ;  
    }  
  
    /* release the dynamic semaphore */  
    rt_sem_release(dynamic_sem);  
    /* wait forever */  
    result = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER);  
  
    if (result != RT_EOK)  
    {  
        rt_kprintf("take a dynamic semaphore, failed.\n");  
        rt_sem_delete(dynamic_sem);  
        return ;  
    }  
  
    rt_kprintf("take a dynamic semaphore, done.\n");  
    /* delete the semaphore*/  
    rt_sem_delete(dynamic_sem);  
}  
//static rt_thread_t tid = RT_NULL;  
int rt_application_init()  
{  
    rt_err_t  result;  
  
    result = rt_sem_init(&static_sem,  
        "ssem",  
        0, RT_IPC_FLAG_FIFO);  
    if (result != RT_EOK)  
    {  
        rt_kprintf("init static semaphore failed. \n");  
        return -1;  
    }  
  
    dynamic_sem = rt_sem_create("dsem",  
        0, RT_IPC_FLAG_FIFO);  
    if (dynamic_sem == RT_NULL)  
    {  
        rt_kprintf("create dynamic semaphore failed. \n");  
        return -1;  
    }  
  
    /* thread1 init */  
    rt_thread_init(&thread1,   
        "t1",   
        rt_thread_entry1, RT_NULL,  
        &thread1_stack[0], sizeof(thread1_stack),  
        11, 5  
        );  
    rt_thread_startup(&thread1);  
  
    return 0;  
}  

结果为:

take semaphore timeout  
take a staic semaphore, done.  
take semaphore timeout  
take a dynamic semaphore, done.  

 信号量使用场合:

  信号量是一种非常灵活的同步方式,可以运用在多种场合中。形成锁,同步,资源计数等关系,也能方便的用于线程与线程,中断与线程的同步中。

  1、线程同步

  线程同步是信号量最简单的一类应用。例如,两个线程用来进行任务间的执行控制转移,信号量的值初始化成具备0个信号量资源实例,而等待线程先直接在这个信号量上进行等待。

当信号线程完成它处理的工作时,释放这个信号量,以把等待在这个信号量上的线程唤醒,让它执行下一部分工作。这类场合也可以看成把信号量用于工作完成标志:信号线程完成它自己的工作,然后通知等待线程继续下一部分工作。

  2、锁

  锁,单一的锁常应用于多个线程间对同一临界区的访问。信号量在作为锁来使用时,通常应将信号量资源实例初始化成1,代表系统默认有一个资源可用。当线程需要访问临界资源时,它需要先获得这个资源锁。当这个线程成功获得资源锁时,其他打算访问临界区的线程将被挂起在该信号量上,这是因为其他线程在试图获取这个锁时,这个锁已经被锁上(信号量值是0)。当获得信号量的线程处理完毕,退出临界区时,它将会释放信号量并把锁解开,而挂起在锁上的第一个等待线程将被唤醒从而获得临界区的访问权。

因为信号量的值始终在1和0之间变动,所以这类锁也叫做二值信号量,如图  所示:

锁

  3、中断与线程的同步

信号量也能够方便的应用于中断与线程间的同步,例如一个中断触发,中断服务例程需要通知线程进行相应的数据处理。这个时候可以设置信号量的初始值是0,线程在试图持有这个信号量时,由于信号量的初始值是0,线程直接在这个信号量上挂起直到信号量被释放。 当中断触发时,先进行与硬件相关的动作,例如从硬件的I/O口中读取相应的数据,并确认中断以清除中断源,而后释放一个信号量来唤醒相应的线程以做后续的数据处理。例如finsh shell线程的处理方式,如图 finsh shell的中断、线程间同步 所示:

finsh shell的中断、线程间同步

semaphore先初始为0,而后shell线程试图取得信号量,因为信号量值是0,所以它会被挂起。当console设备有数据输入时,将产生中断而进入中断服务例程。在中断服务例程中,它会读取console设备的数据,并把读得的数据放入uart buffer中进行缓冲,而后释放信号量,释放信号量的操作将唤醒shell线程。在中断服务例程运行完毕后,如果系统中没有比shell线程优先级更高的就绪线程存在时,shell线程将持有信号量并运行,从uart buffer缓冲区中获取输入的数据。

  • 警告: 中断与线程间的互斥不能采用信号量(锁)的方式,而应采用中断锁。

  4、资源计数

资源计数适合于线程间速度不匹配的场合,这个时候信号量可以做为前一线程工作完成的计数,而当调度到后一线程时,它可以以一种连续的方式一次处理数个事件。例如,生产者与消费者问题中,生产者可以对信号进行多次释放,而后消费者被调度到时能够一次处理多个资源。

  • 注: 一般资源计数类型多是混合方式的线程间同步,因为对于单个的资源处理依然存在线程的多重访问,这就需要对一个单独的资源进行访问、处理,并进行锁方式的互斥操作。

相关文章:

  • 数据库===轻量级mysql数据库管理工具
  • Java类加载器ClassLoader
  • 速查笔记(Linux Shell编程上)
  • Struts2【UI标签、数据回显、资源国际化】
  • [case10]使用RSQL实现端到端的动态查询
  • webpack4入门
  • SSM-Spring-17:Spring中aspectJ注解版
  • 前端Sass回顾以及Compass入门小记
  • ArchSummit2018深圳站筹备中,18大专题征集演讲嘉宾
  • Netty(十八)(中级篇)心跳连接
  • Android7.0以后跨应用
  • 手机零部件下半年持续缺货 供应商涨价趋势加剧
  • 1005. 继续(3n+1)猜想 (25)
  • [UWP小白日记-1]判断APP是否是第一次运行初始化SQLITE数据库
  • 微服务的4个设计原则和19个解决方案
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • centos安装java运行环境jdk+tomcat
  • GitUp, 你不可错过的秀外慧中的git工具
  • JavaScript设计模式与开发实践系列之策略模式
  • JavaScript异步流程控制的前世今生
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • MySQL几个简单SQL的优化
  • Protobuf3语言指南
  • uni-app项目数字滚动
  • 程序员该如何有效的找工作?
  • 从setTimeout-setInterval看JS线程
  • 读懂package.json -- 依赖管理
  • 让你的分享飞起来——极光推出社会化分享组件
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • 小程序 setData 学问多
  • 一个SAP顾问在美国的这些年
  • 积累各种好的链接
  • ​MPV,汽车产品里一个特殊品类的进化过程
  • $GOPATH/go.mod exists but should not goland
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (论文阅读32/100)Flowing convnets for human pose estimation in videos
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • .java 指数平滑_转载:二次指数平滑法求预测值的Java代码
  • .NET 5.0正式发布,有什么功能特性(翻译)
  • .Net core 6.0 升8.0
  • .net core控制台应用程序初识
  • .net 生成二级域名
  • .NetCore部署微服务(二)
  • .ui文件相关
  • @serverendpoint注解_SpringBoot 使用WebSocket打造在线聊天室(基于注解)
  • [.net] 如何在mail的加入正文显示图片
  • [Android View] 可绘制形状 (Shape Xml)
  • [Angular 基础] - 自定义指令,深入学习 directive
  • [C#]OpenCvSharp结合yolov8-face实现L2CS-Net眼睛注视方向估计或者人脸朝向估计
  • [C#]winform使用引导APSF和梯度自适应卷积增强夜间雾图像的可见性算法实现夜间雾霾图像的可见度增强
  • [C++]:for循环for(int num : nums)
  • [C++]四种方式求解最大子序列求和问题
  • [C++数据结构](31)哈夫曼树,哈夫曼编码与解码