简洁介绍信号量sem_t的起因、原理与使用

起因:

对应用程序优化加速时,会使用多线程技术来进行加速。使用软件多线程可以让用户与底层硬件进行隔离。即不管实际底层硬件是否有多个执行单元,都可以使用多线程来达到充分利用cpu资源,减少cpu等待延时的目的。

多线程在用户看来是并行执行的。在单执行单元的硬件平台上虽然在内核内多个线程之间是串行占用cpu资源并由cpu调度策略决定多个线程的执行顺序,但是从用户态看来多线程是并行执行的。而多执行单元的硬件平台上,多线程确实可以达到真实的并行执行。

同一个应用程序,不同线程上运行的代码不可能完全独立无关,即线程A上运行的代码codeA与线程B上运行的代码codeB总会有交互的需求。可以是执行顺序上的交互(执行前后关系),也可以是数据上的交互(访问修改相同的数据结构等)。

线程之间的交互需求可以通过多种技术来完成,信号量就是其中一种线程交互技术, 一般用来进行线程间的执行顺序上的交互。其他线程交互技术以后有空再谈。

原理:

信号量的发明非常简洁。可以将sem_t信号量类型看作是一个int类型typedef, 只不过程序员之间对这个变量类型具有约定的使用规则。全部相关函数只有下面几个,在头文件#include<semaphore.h>中进行声明。

声明一个信号量:sem_t  sem_var;

  1. int sem_init(sem_t *p_sem_var,int share_flag,unsigned int value);

  2. int sem_wait(sem_t *p_sem_var);

  3. int sem_trywait(sem_t *p_sem_var);

  4. int sem_post(sem_t *p_sem_var);

  5. int sem_destroy(sem_t *p_sem_var)用来释放信号量sem。 

1. sem_init():用于对指定信号初始化(*sem_var = value)。share_flag==0,表示信号在当前进程的多个线程之间共享;share_flag!=0,信号量在进程间共享。我们经常使用到的都是==0的情况。value表示初始化信号的值。

2.sem_wait():读取当前信号量的值sem_var并进行如下判断和操作:

(1) if sem_var >0, then *p_sem_var--;并执行本线程sem_wait语句之后的代码。

(2) if sem_var <=0, then  本线程执行暂停sem_wait后的代码,直到sem_var>0。一旦系统检测到sem_var重新>0则进行(1)的操作。

我们把线程因为某个信号量<=0的原因而停止执行后续代码的行为叫做线程阻塞

3.sem_trywait():读取当前信号量的值sem_var当大于0的时候p_sem_var--。<=0的时候不做操作。不管哪种情况,该线程都继续执行后续代码。可以看出sem_trywait是sem_wait的非阻塞版本哦。

4.sem_post():读取当前信号量的值sem_var并 *p_sem_var++。

试想一下,如果当前p_sem_var=0, 且已经有2个线程thread_a, thread_b阻塞在sem_var。那么某个线程thread_c调用sem_post函数的同时,与thread_c并行执行的thread_a, thread_b中的一个会因为检测到sem_var为1而停止阻塞。当有多个线程阻塞在这个信号量上时,调用一次sem_post只会使其中的一个线程不再阻塞。具体是哪个线程不再阻塞,则由线程的调度策略决定。

5.sem_destroy() :销毁一个p_sem_var指向的信号量。仍有process或thread被阻塞(blocked)在这个sem_t的时候, 函数会返回错误码。

------------------------------------------------------------------------------------------------------------------------------

sem_wait(), sem_trywait(),sem_post()在计算机系统内部实现的时候都是一个“原子操作”。原子操作概念是否理解并不影响用户对信号量的使用。

原子操作,简单理解就是内核采用某种技术可以使得这个操作具有独立不可分割性。回到信号量就是对信号量的加减操作总是能够准确执行。多个线程同时调用原子操作的信号量操作函数的时候不会引起冲突而导致sem_var的值偏离用户的期望。即n个线程同时调用对同一个信号量做加“1”操作的函数后,sem_var确实就等于+=n的值,而不是出现+=n-1之类的错误结果值。

 使用举例:

有些人可能不太理解,计算机执行1+1难道会出现不等于2的结果吗?答案是确实。想象一下,如果线程A和线程B,都可以自由访问修改同一个地址存放的变量int i。int *p_i = &i。i此时等于0。然后在下一刻时间点如果两个线程都执行*p_i+=1的操作。那i会等于多少?我们知道+=操作对应到底层是一条计算机指令。为了执行这条执行需要进行取指令,译码,执行,存储等n个步骤。用户看起来并行执行的两个线程在深入到底层硬件的执行的某个最小不可分割的时刻并非并行,总是会存在先后顺序。对比下面2个线程A和线程B执行*p_i+=1的操作顺序的合理情形可以看出i的最终结果值是用户不可预测的。2个线程尚且如此,何况当存在16个或更多个线程的时候i的值就会千奇百怪啦:

(0)case0:

thread A读取*p_i处i的当前值,并放在寄存器中得到regA=0;

thread B读取*p_i处i的当前值,并放在寄存器中得到regB=0;

thread A给寄存器regA中的值执行+1的操作后regA等于1;

thread B给寄存器regB中的值执行+1的操作后regB等于1;

threadA将寄存器regA的值1写回到地址*p_i处, 使得i等于1;

threadA将寄存器regB的值1写回到地址*p_i处, 使得i等于1;

(1)case1:

thread A读取*p_i处i的当前值,并放在寄存器中得到regA=0;

thread A给寄存器regA中的值执行+1的操作后regA等于1;

threadA将寄存器regA的值1写回到地址*p_i处, 使得i等于1;

thread B读取*p_i处i的当前值,并放在寄存器中得到regB=1;

thread B给寄存器regB中的值执行+1的操作后regB等于2;

threadA将寄存器regB的值1写回到地址*p_i处, 使得i等于2;

所以, 可以看出为了正确的完成多个线程对相同共有变量的一系列操作行为。就必须明确规定其先后顺序。此时信号量就可以派上用场了。

----------------------------------------------------------------------------------

如何修改上面的代码使得用户得到期望的i=2呢?

原始代码:

thread A:

{

*p_i+=1;

}

threadB:

{

*p_i+=1;

}

修改成下面的:

sem_t semA;//声明一个信号量

sem_init(&semA, 0, 0);//信号量初始化

thread A:

{

sem_wait();//等待信号量大于0

*p_i+=1;

}

thread B:

{

*p_i+=1;

sleep(5);

sem_post();//将信号量+1

}

【 转载、摘抄本文请 注明出处 并对本文 点赞,哈哈~~~。有需要请联系微信号81469486@qq.com】

简洁介绍信号量sem_t的起因、原理与使用相关推荐

  1. 【原创】ucos信号量的操作及原理

    信号量的操作及原理   1.OSSemCreate创建信号量semaphore     在使用信号量之前,要先用OSSemCreate创建一个信号量,并通过返回的合法事件结构体指针使用信号量. OS_ ...

  2. Linux信号量 sem_t简介

    简介请移步: https://blog.csdn.net/qq_19923217/article/details/82902442 https://blog.csdn.net/evsqiezi/art ...

  3. 简单介绍LC振荡电路的工作原理及特点

    简单介绍LC振荡电路的工作原理及特点 LC振荡电路,顾名思义就是用电感L和电容C组成的一个选频网络的振荡电路,这个振荡电路用来产生一种高频正弦波信号.常见的LC振荡电路有好多种,比如变压器反馈式.电感 ...

  4. [硬件] 简单介绍磁盘结构及工作原理

    一.前言 最近学习DOS下的汇编语言用到了很多与硬件相关的指令,比如上一期写的int 13h(直接磁盘服务),其中接口参数中就有驱动器号,磁头,磁道,扇区的概念,对于一个计算机组成原理丢了一年的人来说 ...

  5. java多线程的同步控制_Java多线程并发控制工具信号量Semaphore,实现原理及案例...

    信号量(Semaphore)是Java多线程兵法中的一种JDK内置同步器,通过它可以实现多线程对公共资源的并发访问控制.一个线程在进入公共资源时需要先获取一个许可,如果获取不到许可则要等待其它线程释放 ...

  6. ansible自动化运维工具的详细介绍、使用、工作原理、安装方式、与被管理节点建立信任关系等等

    ansible简介 Ansible 是一个IT自动化工具,它能够配置系统.部署软件.编排更复杂的it任务,如连续部署或零停机时间滚动更新 Ansible用python编写,尽管市面上已经又很多可供选择 ...

  7. 简单粗暴理解与实现机器学习之逻辑回归:逻辑回归介绍、应用场景、原理、损失以及优化...

    作者 | 汪雯琦 责编 | Carol 来源 | CSDN 博客 出品 | AI科技大本营(ID:rgznai100) 学习目标 知道逻辑回归的损失函数 知道逻辑回归的优化方法 知道sigmoid函数 ...

  8. mysql序列号生成_超详细的mysql数据库GTID介绍—概念、优缺点、原理、生命周期等

    概述 这几天就简单介绍一下GTID好了~这篇是概念篇.. 从MySQL 5.6.5 开始新增了一种基于 GTID 的复制方式.通过 GTID 保证了每个在主库上提交的事务在集群中有一个唯一的ID.这种 ...

  9. IPv6基础介绍--IPv6路由基础--DHCPv6原理与配置——总结

    一.IPv6基础介绍 1.IPv6是Internet工程任务组(IETF)设计的一套规范,它是网络层协议的第二代标准协议,也是IPv4(Internet Protocol Version 4)的升级版 ...

最新文章

  1. 《Essential ASP.NET 2.0中文版》
  2. Python3--批量爬取数据之调用有道api进行翻译
  3. 两个时间计算毫秒在线_蹲坑英语时间之in a jiffy
  4. SharePoint Server 2007 简单安装指南
  5. payara 创建 集群_Apache Payara:让我们加密
  6. 用python找出所有三位数中的水仙花数_python使用循环打印所有三位数水仙花数的实例...
  7. 宋森安——CHARLS中国健康与养老调查数据清洗(一)
  8. virtualbox安装Windows 7 64位旗舰版 (包含镜像文件)
  9. 进行日常记账后,怎样导出表格
  10. 嘉兴学院c语言期末考试题库,液压与气压传动(嘉兴学院)知到APP答案
  11. 关于前端一个用于设置渐变色的css代码网站
  12. Flink 多流转换
  13. 成功案例 | 助力贵州省国家税务局从VMware无缝迁移至国产化安超云平台
  14. LeetCode881:救生艇 (C、C++实现)
  15. 计算机可以实现u盘和硬盘格式化,“资源管理器”和“计算机”窗口都可以实现U盘和硬盘格式化...
  16. 信用评分卡Credit Scorecards (1-7)
  17. java课程设计计算器 uml简图,计算器的用例建模
  18. 使用STAF进行自动化安装测试
  19. 如何最大程度的提高效率?ie分析软件为效率提高带来无限可能
  20. python 爬取5566图库图片

热门文章

  1. 广播风暴的产生原因和原理是什么?
  2. Linux终端突然打不开,【SOLVED】ubuntu 误操作致使打不开Terminal
  3. VM log是什么?EPL又是什么?
  4. Linux驱动开发之IIC驱动实验【完整教程】
  5. 2012威盛软件类面试(一上午三轮)
  6. fastapi 如何响应文件下载
  7. 计算机操作员中级上机,计算机操作员中级上机(范文).doc
  8. 作为一个Android程序员,关于音视频开发,这些你确定这些你都懂了吗
  9. SAP 成本结算中 费用分割的理解和用法
  10. 基于Q-learning的无人机三维路径规划(含完整C++代码)