一、信号量基本原理

信号量概念由荷兰科学家Dijkstra首先提出。信号量是一个特殊类型的变量,它可以被增加或者减少。但对其的关键访问被保证是原子操作,即使在一个多线程程序中也是如此。

信号量有两种类型:
  1.二进制信号量。它只有0和1两种取值。
  2.计数信号量。它可以有更大的取值范围。
  
  如果要用信号量来保护一段代码,使其每次只能被一个执行线程运行,就要用到二进制信号量。
  如果要允许有限数目的线程执行一段指定的代码,就需要用到计数信号量。

二、信号量基本操作

信号量基本操作如下表所示:

在使用信号量时需要包含头文件,如下所示:

#include<semaphore.h>

1.初始化信号量
  在使用信号量之前,需要定义信号量,定义信号量的代码如下:

sem_t sem;

sem_init()函数用于初始化信号量,其函数声明如下:

int sem_init (sem_t *sem , int pshared, unsigned int value);

第1个参数sem为指向信号量结构的一个指针;
  第2个参数pshared表示是否为多进程共享当前信号量。Linux Threads没有实现多进程共享信号量,因此pshared必须为0,所有非0值的pshared输入都将使sem_init()返回-1,且置errno为ENOSYS;
  第3个参数value给出了信号量的初始值。

2.等待信号量
  sem_wait()函数以阻塞的方式等待信号量,若value的值大于0会将value的值减1,然后继续向下执行。若value的值为0会一直等待,直到value的值大于0。其函数声明如下所示:

int sem_wait(sem_t *sem);

sem_trywait()函数以非阻塞方式等待信号量。若value的值大于0,会将value的值减1,然后返回0。若value的为0,则返回-1。其函数声明如下所示:

int sem_trywait(sem_t *sem);

3.释放信号量
  sem_post()函数用于释放信号量。此函数会将value的值加1,然后通知其他等待该信号量的线程。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不再阻塞,选择机制同样是由线程的调度策略决定的。函数声明如下所示:

int sem_post(sem_t *sem);

该函数成功时返回 0;错误时,信号量的值没有更改,返回-1 ,并设置errno 来指明错误。

4.获取信号量的值
  sem_getvalue()函数用于获取信号量的值。函数声明如下所示:

int sem_getvalue(sem_t* sem, int* sval)

第1个参数sem为指向信号量结构的一个指针;
  第2个参数用于返回信号量的值。

该函数执行成功返回0,错误时,返回-1 ,并设置errno 来指明错误。
  
5.销毁信号量
  sem_destroy()函数用来销毁信号量,其函数声明如下:

int sem_destroy(sem_t *sem);

参数sem为指向信号量结构的一个指针。

三、示例

下面通过信号量来实现生产者,消费者模问题。生产者消费者问题是一个经典的数学问题,要求生产者-消费者在固定仓库空间条件下,生产者每生产一个产品将占用一个仓库空间,生产者生产的产品库存不能越过仓库的存储量,消费者每消费一个产品将增加一个仓库空间,消费者在仓库产品为0时不能再消费。
  
下面为示例代码:

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>sem_t sem_product;      // 产品信号量
sem_t sem_space;        // 仓库容量信号量void* pth_consume(void* arg)
{char* name = (char*)arg;int cnt;while (1) {sem_wait(&sem_product);sem_post(&sem_space);sem_getvalue(&sem_product, &cnt);printf("consume thread %s consumption a product, production count:%d\n", name, cnt);        sleep(2);   // 每隔2S消费一个产品}pthread_exit(0);
}void* pth_product(void* arg)
{char* name = (char*)arg;int cnt;while (1) {sem_wait(&sem_space);sem_post(&sem_product);sem_getvalue(&sem_product, &cnt);printf("product thread %s produce a product, production count:%d\n", name, cnt);            sleep(1);   // 每隔1S生产一个产品}pthread_exit(0);
}int main()
{  pthread_t tid1, tid2, tid3, tid4;int ret;// 初始化信号量sem_init(&sem_product, 0, 0);  // 初始状态产品个数为0sem_init(&sem_space, 0, 5);        // 初始状态仓库空间为5ret = pthread_create(&tid1, NULL, pth_product, "A");if (0 != ret) {perror("create pthead error");return -1;}ret = pthread_create(&tid2, NULL, pth_product, "B");if (0 != ret) {perror("create pthead error");return -1;}ret = pthread_create(&tid3, NULL, pth_consume, "C");if (0 != ret) {perror("create pthead error");return -1;}ret = pthread_create(&tid4, NULL, pth_consume, "D");if (0 != ret) {perror("create pthead error");return -1;}pthread_join(tid1, NULL);pthread_join(tid2, NULL);pthread_join(tid3, NULL);pthread_join(tid4, NULL);// 销毁信号量sem_destroy(&sem_product);sem_destroy(&sem_space);return 0;
}

运行结果如下图所示:

Linux线程同步机制四--信号量sem相关推荐

  1. c++ linux 线程等待与唤醒_C++ Linux线程同步机制:POSIX信号量,互斥锁,条件变量...

    线程同步机制:POSIX 信号量,互斥量,条件变量 POSIX 信号量 常用的POSIX 信号量函数为如下5个: sem_init sem_destroy sem_wait sem_trywait s ...

  2. Linux内核同步机制之信号量与锁

    Linux内核同步控制方法有很多,信号量.锁.原子量.RCU等等,不同的实现方法应用于不同的环境来提高操作系统效率.首先,看看我们最熟悉的两种机制--信号量.锁. 一.信号量 首先还是看看内核中是怎么 ...

  3. Linux 多线程同步机制:互斥量、信号量、条件变量

    互斥量:互斥量提供对共享资源的保护访问,它的两种状态:lock和unlock,用来保证某段时间内只有一个线程使用共享资源,互斥量的数据类型是pthread_mutex_t 主要涉及函数:pthread ...

  4. Linux——线程同步(条件变量、POSIX信号量)和线程池

    一.线程同步 (一).概念 线程同步是一种多线程关系,指的是线程之间按照特定顺序访问临界资源,进而能够避免线程饥饿问题. 所谓线程饥饿指的是某个线程长期"霸占"临界资源,导致其他线 ...

  5. Linux中的线程同步机制-futex

    Linux中的线程同步机制(一) -- Futex 引子 在编译2.6内核的时候,你会在编译选项中看到[*] Enable futex support这一项,上网查,有的资料会告诉你"不选这 ...

  6. Linux内核同步机制之(四):spin lock【转】

    转自:http://www.wowotech.net/kernel_synchronization/spinlock.html 一.前言 在linux kernel的实现中,经常会遇到这样的场景:共享 ...

  7. 学习java的第四十天,线程的优先级、守护线程、线程同步机制、死锁

    一.线程的优先级(priority) Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行. 线程的优先级用数字表示,范围1~10 Thr ...

  8. linux线程同步的方法

    #Linux 线程同步的三种方法 线程的最大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点.linux下提供了多种方式来处理线程同步,最常用的是互斥锁.条件变量和信号量. ##一.互斥锁 ...

  9. 01 线程同步机制封装类

    01 线程同步机制封装类 RAII RAII全称是"Resource Acquisition is Initialization",直译过来是"资源获取即初始化" ...

最新文章

  1. xray 被动_长亭xray:一款自动化Web漏洞扫描神器(免费社区版)
  2. 任正非谈鸿蒙系统失误,谷歌也没想到会来的这么快,任正非谈鸿蒙:系统不难,生态快完善...
  3. ajax form表单提交_开发日志:金数据表单自动提交脚本
  4. 苹果或将推中国特色版iPhone 削掉了Face ID改用屏幕指纹
  5. 【clickhouse】clickhouse 副本与分片 分片详解
  6. html中css修改字体,CSS字体设置 DIV内字体设置
  7. [C++对象模型][10]类型转化
  8. 软件工程革命 三部曲 —— 前传
  9. python︱利用dlib和opencv实现简单换脸、人脸对齐、关键点定位与画图
  10. xsd文件转图片_原来华为手机能一键将图片转为PDF,实在太方便啦!你还不会吗?...
  11. Spring Security学习
  12. pythonjson格式化输出_pythonjson格式化输出_Python json格式化打印实现过程解析
  13. python控制浏览器最小化_如何启动最小化的Selenium Firefox web浏览器?
  14. Mac上的全局翻译利器 : Bob + PopClip
  15. 论文阅读:Deep learning—Yann LeCun, Yoshua Bengio Geoffrey Hinton
  16. ai人工智能语音分析系统_语音应用搜索正在改变语音AI是领先者
  17. c语言源代码三角函数大全,【C语言及程序设计】项目2-9-3:编制三角函数表
  18. macOS Ventura 13.0 (22A380) 正式版发布,ISO、IPSW、PKG 下载
  19. 安装Altera USB-Blaster驱动程序遇到的问题
  20. LOJ10064黑暗城堡

热门文章

  1. mysql 加密狗_ESXI 5.1/5.5 主机添加或映射USB设备(加密狗)
  2. 盒子模型之京东快报页面
  3. Fiddler抓包学习笔记
  4. 国标GB28181(EasyGBS)/RTSP/HIKSDK/EHOME协议视频智能分析平台EasyCVR人脸识别智能分析功能拓展
  5. 微信小程序开发知识点总结
  6. 基于STM32F103设计的智能门锁(支持多种开锁解锁方式)
  7. ICON艾肯VST声卡驱动官方原版新款ProDriver-4.0.1全系列下载安装教程
  8. 进程互斥以及进程互斥实现方法(包含代码)
  9. 信息学奥赛一本通 1374:铲雪车(snow)
  10. VScode 插件live preview无法显示背景图片/图片