信号量(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[];
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, ); if (result == -RT_ETIMEOUT)
{
if (rt_tick_get() - tick != )
{
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, );
if (result == -RT_ETIMEOUT)
{
if (rt_tick_get() - tick != )
{
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",
, RT_IPC_FLAG_FIFO);
if (result != RT_EOK)
{
rt_kprintf("init static semaphore failed. \n");
return -;
} dynamic_sem = rt_sem_create("dsem",
, RT_IPC_FLAG_FIFO);
if (dynamic_sem == RT_NULL)
{
rt_kprintf("create dynamic semaphore failed. \n");
return -;
} /* thread1 init */
rt_thread_init(&thread1,
"t1",
rt_thread_entry1, RT_NULL,
&thread1_stack[], sizeof(thread1_stack),
,
);
rt_thread_startup(&thread1); return ;
}

结果为:

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的中断、线程间同步 所示:

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

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

  4、资源计数

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

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

RT-Thread信号量使用(动态/静态信号量) 及 信号量的四种使用场合的更多相关文章

  1. javascript 动态修改css样式方法汇总(四种方法)

    在很多情况下,都需要对网页上元素的样式进行动态的修改.在JavaScript中提供几种方式动态的修改样式,下面将介绍方法的使用.效果.以及缺陷. 1.使用obj.className来修改样式表的类名. ...

  2. Linux 信号量之Posix有名字的信号量

    信号量(semaphore),也和互斥锁一样提供了线程间或者进程间的同步功能. 信号量有三种: Posix有名字的信号量 Posix基于内存的信号量 System V信号量 信号量比互斥锁高级,互斥锁 ...

  3. Linux下快速静态编译Qt以及Qt动态/静态版本共存

    Qt下静态编译Qt,根据我的经验,如果按照Windows下那种直接拿官方sdk安装之后的文件来编译是行不通的,需要直接下载Qt的source包,目前诺基亚的源码叫做qt-everywhere-open ...

  4. Java代理设计模式(Proxy)的四种具体实现:静态代理和动态代理

    面试问题:Java里的代理设计模式(Proxy Design Pattern)一共有几种实现方式?这个题目很像孔乙己问"茴香豆的茴字有哪几种写法?" 所谓代理模式,是指客户端(Cl ...

  5. Linux下快速静态编译Qt以及Qt动态/静态版本共存(提供了编译4.6,5.6的精通编译脚本,并且apt-get install 需要的库也全列出来了。还有分析问题的心理过程)good

    qt4.6 Linux./configure -static -release -confirm-license -opensource -qt-zlib -qt-libpng -qt-libjpeg ...

  6. Python 笔试集(3):编译/解释?动态/静态?强/弱?Python 是一门怎样的语言

    面试题 解释/编译?动态/静态?强/弱?Python 到底是一门怎样的语言? 编译 or 解释? 编译.解释都是指将(与人类亲和的)编程语言翻译成(计算机能够理解的)机器语言(Machine code ...

  7. RT Thread 通过ENV来配置SFUD,操作SPI Flash

    本实验基于正点原子stm32f4探索者板子 请移步我的RT Thread论坛帖子. https://www.rt-thread.org/qa/forum.php?mod=viewthread& ...

  8. 四种java代码静态检查工具

    [转载]常用 Java 静态代码分析工具的分析与比较 转载自 开源中国社区 http://www.oschina.net/question/129540_23043       1月16日厦门 OSC ...

  9. es6 Object.assign ECMAScript 6 笔记(六) ECMAScript 6 笔记(一) react入门——慕课网笔记 jquery中动态新增的元素节点无法触发事件解决办法 响应式图像 弹窗细节 微信浏览器——返回操作 Float 的那些事 Flex布局 HTML5 data-* 自定义属性 参数传递的四种形式

    es6 Object.assign   目录 一.基本用法 二.用途 1. 为对象添加属性 2. 为对象添加方法 3. 克隆对象 4. 合并多个对象 5. 为属性指定默认值 三.浏览器支持 ES6 O ...

随机推荐

  1. js 显示当前系统时间

    <div id="test"></div>                <script >                    setInt ...

  2. 1-2:CSS3课程入门之结构选择

    E:nth-child(n) 表示E父元素中的第n个字节点 p:nth-child(odd){background:red}/*匹配奇数行*/ p:nth-child(even){background ...

  3. EntityFramework走马观花之CRUD(下)

    我在Entity Framework系列文章的CRUD上篇中介绍了EF的数据查询,中篇谈到了EF的数据更新,下篇则聊聊EF实现CRUD的内部原理. 跟踪实体对象状态 在CRUD上篇和中篇谈到,为了实现 ...

  4. EasyNVR H5无插件摄像机直播解决方案前端解析之:关于直播页面和视频列表页面切换的问题

    关于直播页面和视频列表页面切换 为了给用户更好的用户体验,并且更好的让用户快速简洁的了解实时的视频直播信息.一般多会分为列表展示和实时的视频直播展示. 表面上只是两个视图之间的随意切换,其实切换的两个 ...

  5. iOS UIWebview添加请求头的两种方式

    1.在UIWebviewDelegate的方法中拦截request,设置request的请求头,废话不多说看代码: - (BOOL)webView:(UIWebView *)webView shoul ...

  6. 微信小程序设计指南

    微信小程序设计指南 · 小程序 https://developers.weixin.qq.com/miniprogram/design/index.html

  7. vue+vuex构建单页应用

    基本 构建工具: webpack 语言: ES6 分号:行首分号规则(行尾不加分好, [ , ( , / , + , - 开头时在行首加分号) 配套设施: webpack 全家桶, vue 全家桶 项 ...

  8. 【题解】[CJOI2019]Cipher

    [题解][CJOI2019]Cipher 题目描述 给定你\(p\)进制数\(s\),\(p \le 9+26\),求对于十进制数\(k\),求\(k^s \equiv ? \mod m\) 数据范围 ...

  9. python 的print 用法

    print(x,y) 等价于 import sys sys.stdout.write(str(x) + ' ' +str(y) + '\n') 从语法上讲,调用python3.0 的print 函数有 ...

  10. update module (更新模块)

    [转自http://blog.csdn.net/zhongguomao/article/details/6712568] function module:更新程序必须用一个特殊的FM(update m ...