​​Linux 信号量​​​​

news/2024/4/19 0:04:40

 


信号量的产生原因

我们将可能会被多个执行流同时访问的资源叫做临界资源,临界资源需要进行保护否则会出现数据不一致等问题。

当我们仅用一个互斥锁对临界资源进行保护时,相当于我们将这块临界资源看作一个整体,同一时刻只允许一个执行流对这块临界资源进行访问。

但实际我们可以将这块临界资源再分割为多个区域,当多个执行流需要访问临界资源时,如果这些执行流访问的是临界资源的不同区域,那么我们可以让这些执行流同时访问临界资源的不同区域,此时不会出现数据不一致等问题。

举个栗子:

假设临界资源是一块数组:

vector<int>nums(5,1)

 互斥锁的要求是只能访问这个数组nums,信息量可以允许A执行流访问nums[1];B执行流访问nums[2],只要下标是不一样的,其他执行流都可以访问数组nums。

信号量的概念

信号量又叫做信号灯,其本质是一个支持PV操作的计数器。POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。

信号量函数

初始化

SEM_INIT(3)                     Linux Programmer's Manual                    SEM_INIT(3)NAMEsem_init - initialize an unnamed semaphoreSYNOPSIS#include <semaphore.h>int sem_init(sem_t *sem, int pshared, unsigned int value);Link with -pthread.
参数:
pshared:0表示线程间共享,非零表示进程间共享
value:信号量初始值

销毁


NAMEsem_destroy - destroy an unnamed semaphoreSYNOPSIS#include <semaphore.h>int sem_destroy(sem_t *sem);Link with -pthread.

等待

NAMEsem_wait, sem_timedwait, sem_trywait - lock a semaphoreSYNOPSIS#include <semaphore.h>int sem_wait(sem_t *sem);int sem_trywait(sem_t *sem);int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);Link with -pthread.Feature Test Macro Requirements for glibc (see feature_test_macros(7)):sem_timedwait(): _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600

功能:等待信号量,会将信号量的值减1

发布

NAMEsem_post - unlock a semaphoreSYNOPSIS#include <semaphore.h>int sem_post(sem_t *sem);Link with -pthread.

功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。

二元信号量模拟实现互斥功能

信号量本质是一个计数器,如果将信号量的初始值设置为1,那么此时该信号量叫做二元信号量。

基于环形队列的生产消费模型

环形队列采用数组模拟,用模运算来模拟环状特性

环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来 判断满或者空。另外也可以预留一个空的位置,作为满的状态

生产者和消费者申请和释放资源

空间资源和数据资源

代码上用 data_sem和space_sem分别表示数据资源和空间资源

生产者:生产者关注的是空间资源,只要环形队列中有空间,他就可以生产。

=》生产者申请空间资源,释放数据资源  

让我们来分析一下生产者具体需要做什么:

1.如果blank_sem的值不为0,则信号量申请成功,此时生产者可以进行生产操作。

2.如果blank_sem的值为0,则信号量申请失败,此时生产者需要在blank_sem的等待队列下进行阻塞等待,直到环形队列当中有新的空间后再被唤醒

消费者:消费者关注的是数据资源,只要有数据就能消费

=》消费者申请数据资源,释放空间资源

消费者有具体怎么做呢?

虽然生产者在进行生产前是对blank_sem进行的P操作,但是当生产者生产完数据,应该对data_sem进行V操作而不是blank_sem。
生产者在生产数据前申请到的是blank位置,当生产者生产完数据后,该位置当中存储的是生产者生产的数据,在该数据被消费者消费之前,该位置不再是blank位置,而应该是data位置。
当生产者生产完数据后,意味着环形队列当中多了一个data位置,因此我们应该对data_sem进行V操作。

必须遵守的两个规则

第一个规则:生产者和消费者不能对同一个位置进行访问。(这个是显然的,如果同时访问同一个位置的数据,可能会产生意外的错误)

第二个规则:无论是生产者还是消费者,都不应该将对方套一个圈以上。

(我们通过信号量适当地让消费者线程和生产者线程相互切换)

信号量保护环形队列的原理

代码实现

#include <iostream>
#include <vector>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
const int NUM=16;
class RingQueue
{
public:RingQueue(int _cap = NUM) : q(_cap), cap(_cap){sem_init(&data_sem, 0, 0);sem_init(&space_sem, 0, cap);consume_step = 0;product_step = 0;}void PutData(const int &data){sem_wait(&space_sem); // Pq[consume_step] = data;consume_step++;consume_step %= cap;sem_post(&data_sem); // V}void GetData(int &data){sem_wait(&data_sem);data = q[product_step];product_step++;product_step %= cap;sem_post(&space_sem);}~RingQueue(){sem_destroy(&data_sem);sem_destroy(&space_sem);}private:std::vector<int> q;int cap;sem_t data_sem;sem_t space_sem;int consume_step;int product_step;
};
void *consumer(void *arg)
{RingQueue *rqp = (RingQueue *)arg;int data;while (true){rqp->GetData(data);std::cout << "Consume data done : " << data << std::endl;sleep(1);}
}void *producter(void *arg)
{RingQueue *rqp = (RingQueue *)arg;srand((unsigned long)time(NULL));while (true){int data = rand() % 1024;rqp->PutData(data);std::cout << "Prodoct data done: " << data << std::endl;// sleep(1);}
}
int main()
{RingQueue rq;pthread_t c, p;pthread_create(&c, NULL, consumer, (void *)&rq);pthread_create(&p, NULL, producter, (void *)&rq);pthread_join(c, NULL);pthread_join(p, NULL);
}

更多代码实现参考

RingQueue · fortianyang/StudyForLinux - 码云 - 开源中国 (gitee.com)


http://www.ppmy.cn/news/1046984.html

相关文章

STM32--DMA

文章目录 DMA简介DMA特性 DMA框图DMA基本结构DMA请求数据宽度对齐DMA数据转运工程DMAADC多通道 DMA简介 直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预&#xff0c;数据可以通过DMA快速地移动&#xff0c;这就节省了CPU的…

AgentBench——AI智能体基准测试官方

ModaGPT 简介 排行榜 提交模型 提问 AgentBench是第一个系统性的基准测试,用于评估LLM作为智能体在各种真实世界挑战和8个不同环境中的表现。 Models

Python 网页解析高级篇:深度掌握BeautifulSoup库

在Python的网络爬虫中&#xff0c;BeautifulSoup库是一个强大的工具&#xff0c;用于解析HTML和XML文档并提取其中的数据。在前两篇文章中&#xff0c;我们已经讨论了BeautifulSoup库的基本和中级使用方法&#xff0c;但BeautifulSoup的能力远远超出了这些。在这篇文章中&#…

船舶法兰盘法兰管件3D扫描尺寸测量|三维扫描检测|CAV测量-CASAIM

第一章 服务背景 船舶建造多采用分段建造法&#xff0c;即将零件、预装好的部件在胎架上组合焊接成分段或总段&#xff0c;然后由船台装配成整船的建造方法。而当船体合拢组装时&#xff0c;在船体上遍布着各种各样的管道&#xff0c;这些管道都需要互相完全适配以确保船体安装…

【算法系列篇】滑动窗口

文章目录 前言什么是滑动窗口1.长度最小的子数组1.1 题目要求1.2 做题思路 1.3 Java代码实现2.无重复字符的最长子串2.1 题目要求2.2 做题思路2.3 Java代码实现 3.最大连续1的个数 III3.1 题目要求3.2 做题思路3.3 Java代码实现 4.将x减到0的最小操作数4.1 题目要求4.2 做题思路…

使用SpringBoot+SpringMVC+Mybatis+WebSocket实现云聊天项目

云聊天 1. 项目介绍 本项目是仿照微信实现网页版聊天程序&#xff0c;用户注册登录后可与在线好友实时聊天&#xff0c;下线好友上线后可以查看到好友发送的消息&#xff1b;用户可以在搜索框搜索用户添加好友&#xff1b;用户还可以查看好友申请列表&#xff0c;选择是否同意…

Unity框架学习--音频管理器

using System.Collections; using System.Collections.Generic; using UnityEngine;/// <summary> /// 音频管理器 /// </summary> public class AudioManager : SingletonMonoAutoBase1_DonDestoryOnLoad<AudioManager> {//各个声道的AudioSource组件AudioS…

深度学习|自监督学习、MAE学习策略、消融实验

前言&#xff1a;最近在阅读论文&#xff0c;发现太多机器学习的知识不懂&#xff0c;把最近看的一篇论文有关的知识点汇总了一下。 自监督学习、MAE学习策略、消融实验 自监督学习MAE学习策略消融实验 自监督学习 Pretrain-Finetune&#xff08;预训练精调&#xff09;模式&…

HDU-7314 2023“钉耙编程”杭电多校赛(4)Data Generation

HDU-7314 2023“钉耙编程”杭电多校赛&#xff08;4&#xff09;Data Generation 题目大意 给定 n n n和 m m m&#xff0c;求下面的算法中 a n s ans ans的期望值模 998244353 998244353 998244353后的结果。 有 T T T组数据。 1 ≤ T ≤ 1 0 5 , 1 ≤ n ≤ 1 0 18 , 0 ≤ m…

更安全,更高效的自学网络安全与黑客技术

学习网络安全&#xff08;黑客技术&#xff09; 网络安全是&#xff1a;黑客技术是&#xff1a;网络安全与黑客技术的关系&#xff1a;自学网络安全学习的误区和陷阱&#xff1a;学习网络安全前期需要准备...学习网络安全中期大致步骤&#xff1a;学习网络安全推荐的学习资料&a…

“开发和运维”只是一个开始,最终目标是构建高质量的软件工程

随着技术的飞速发展&#xff0c;软件行业不断寻求改进和创新的方法来提供更高质量的产品。在这方面&#xff0c;DevOps已经展现出了巨大的潜力。通过打破开发和运维之间的壁垒&#xff0c;DevOps将持续集成、持续交付和自动化流程引入到软件开发中&#xff0c;使团队能够更快地…

引人关注的领域 ---- 信号稀疏表示

本篇文章是博主在人工智能等领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在学习摘录和笔记专…

2308C++协程流程3

参考 #include <协程> #include "简异中.cpp" //用来中文定义的.元<型名 T>构 懒{ 公:构 承诺型;懒(协柄<承诺型>h):柄(h){输出<<"构造同步对象"<<行尾;//.2}~懒(){输出<<"析构同步对象"<<行尾;柄.消…

代码随想录训练营day25| 216.组合总和III 17.电话号码的字母组合

TOC 前言 代码随想录算法训练营day25 一、Leetcode 216.组合总和III 1.题目 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组…

uniapp编写微信小程序和H5遇到的坑总结

uniapp编写微信小程序和H5遇到的坑总结 1、阻止事件冒泡2、二维码生成3、H5跨域配置4、H5时&#xff0c;地址栏上添加版本号5、H5时&#xff0c;tabBar遮挡部分内容6、uniapp使用webview通信6.1、uniapp编写的小程序嵌入h5之间的通信6.1.1、小程序向h5发送消息6.1.2、h5向小程序…

linux文件搜索相关命令

一、find命令用于在目录树中搜索文件和目录的工具。 下面是 find 命令常用选项的用法举例说明&#xff1a; 按名称查找文件&#xff1a; find /path/to/dir -name "filename"&#xff1a;在指定的目录及其子目录中按照文件名称进行查找。 例如&#xff1a;find /hom…

实验二 tftp 服务器环境搭建

tftp 服务器环境搭建 tftp&#xff08;Trivial File Transfer Protocol&#xff09;即简单文件传输协议是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议&#xff0c;提供不复杂、开销不大的文件传输服务。端口号为69 【实验目的】 掌握 tftp 环境搭…

MongDB【CRUD练习-条件查询-文档关系】

练习1-CRUD // 进入test数据库 use test; // 查询文档内容 db.students.find(); // 显示当前数据库中所有集合 show collections; // 向数据库的user集合中插入一个文档 db.users.insertOne({username: "lyh"} ); // 查看当前数据库中所有的集合 发现users集合被创建…

C++ 的关键字(保留字)完整介绍

1. asm asm (指令字符串)&#xff1a;允许在 C 程序中嵌入汇编代码。 2. auto auto&#xff08;自动&#xff0c;automatic&#xff09;是存储类型标识符&#xff0c;表明变量"自动"具有本地范围&#xff0c;块范围的变量声明&#xff08;如for循环体内的变量声明…

2023国赛数学建模B题思路模型代码 高教社杯

本次比赛我们将会全程更新思路模型及代码&#xff0c;大家查看文末名片获取 之前国赛相关的资料和助攻可以查看 2022数学建模国赛C题思路分析_2022国赛c题matlab_UST数模社_的博客-CSDN博客 2022国赛数学建模A题B题C题D题资料思路汇总 高教社杯_2022国赛c题matlab_UST数模社…