Linux--信号量

线程系列:
Linux–线程的认识(一)
Linux–线程的分离、线程库的地址关系的理解、线程的简单封装(二)
线程的互斥:临界资源只能在同一时间被一个线程使用
生产消费模型

信号量

信号量(Semaphore)是在多线程环境下用于控制对共享资源的访问的一种同步机制
它可以确保在同一时刻只有一个线程能够访问共享资源,从而避免多个线程同时修改相同资源而导致的数据竞争和不确定结果。
信号量的主要作用是保护共享资源,确保在同一时刻只有一个线程能够对其进行访问
通过信号量,线程在进入关键代码段之前必须获取信号量,一旦完成了对共享资源的操作,就释放信号量,以允许其他线程访问该资源。

特点

  • 1.同步机制:信号量可用于线程之间的同步,确保在一个线程进入关键代码段时,其他线程必须等待,直到该线程释放信号量。
  • 2.互斥访问:信号量可以实现对共享资源的互斥访问,即同一时刻只允许一个线程访问共享资源,避免了数据竞争。
  • 3.计数器:信号量可以理解为一个计数器,它记录着可用的资源数量。当资源被一个线程占用时,计数器减少;当资源被释放时,计数器增加。

信号量与互斥量的区别

  • 互斥量(Mutex) 是一种用于保护共享资源的锁,它只允许一个线程进入临界区,其他线程必须等待该线程释放锁才能进入。互斥量只有两种状态:锁定和非锁定
  • 信号量 则是一个更为通用的同步原语,它可以控制对一组资源的访问。信号量的值可以大于1,表示同时允许多个线程访问资源,这在某些情况下可能是必要的。信号量的实现原理涉及到原子操作和操作系统的底层支持,在不同的操作系统和编程语言中可能会有不同的实现方式。通常,操作系统提供了对信号量的原子操作支持,以确保在多线程环境下信号量的正确使用。

原子性

原子性(Atomicity)指的是一个或多个操作在执行过程中,不会被其他线程或进程的调度所打断,从而保证了数据的一致性和完整性。

原子性是一个重要的并发编程概念,它确保了一个操作或一组操作在执行时具有不可分割性,即这些操作要么全部执行成功,要么全部不执行,不会出现部分执行的情况。原子操作是不可分割的,要么完全执行,要么不执行,不存在中间状态。

临界资源

临界资源是指一次仅允许一个进程(或线程)使用的共享资源。这类资源如果同时被多个进程访问,可能会导致数据不一致或程序错误

常见的临界资源包括物理设备(如打印机、磁带机等)和软件资源(如消息缓冲队列、全局变量、数组、缓冲区等)。

相关函数

semget()

用于获取一个信号量的标识符

#include<sys/sem.h>
#include<sys/type.h>
#include<sys/ipc.h>int semget(key_t key, int num_sems, int sem_flags);
  • key:这是一个整数值,用于唯一标识一个信号量集。不相关的进程可以通过它访问同一个信号量集。当 key 的值为 IPC_PRIVATE(通常是一个特殊的常量,表示私有)时,会创建一个新的、仅创建者进程可访问的信号量集。若 key 的值是由 ftok() 等函数生成的,且 sem_flags 中包含了 IPC_CREAT 标志,则系统会尝试查找是否存在与 key 值相匹配的信号量集。
  • num_sems:指定在信号量集中需要创建的信号量个数。这个参数在创建信号量集时有效,如果信号量集已存在,则忽略此参数。
  • sem_flags:这是一组标志,用于控制 semget() 的行为。常用的标志包括 IPC_CREAT(如果不存在,则创建信号量集)和 IPC_EXCL(与 IPC_CREAT 结合使用,确保创建的信号量集是唯一的)。此外,sem_flags 的低9位还定义了信号量集的权限,类似于文件的访问权限。

返回成功时,semget() 返回一个正整数,即信号量集的标识符(IPC标识符)。

失败时,返回 -1,并设置 errno 以指示错误原因。

semctl()

用于对信号量的控制

#include <sys/types.h>  
#include <sys/ipc.h>  
#include <sys/sem.h>  int semctl(int semid, int semnum, int cmd, ...);
  • semid:信号量集的标识符,即信号表的索引。
  • semnum:信号集内的索引,用于存取信号集内的某个特定信号量。在某些命令中,此参数可能会被忽略。
  • cmd:指定要执行的控制命令。根据命令的不同,可能需要额外的参数。
  • :对于某些命令,可能需要一个 union semun 类型的参数,用于传递或接收数据。

在这里插入图片描述

成功执行时,根据不同的命令返回不同的非负值。

失败时返回 -1,并设置 errno 以指示错误原因。

semop()

对信号量的操作

#include <sys/sem.h>  int semop(int semid, struct sembuf *sops, unsigned nsops);
  • semid:信号量集的标识符,通过semget()函数获取。
  • sops:指向sembuf结构数组的指针,每个sembuf结构定义了一个要执行的操作。
  • nsops:sops数组中sembuf结构的数量,即要执行的操作数。

sembuf 结构体定义在<sys/sem.h>或<linux/sem.h>中,用于指定对单个信号量的操作:

struct sembuf {  unsigned short sem_num;  /* 信号量的编号 */  short sem_op;            /* 信号量的操作(正/负/零)*/  short sem_flg;           /* 信号量的操作标志 */  
};

返回成功为0,失败为-1;

循环队列

生产消费模型中我们用阻塞队列作为缓冲区,还可以用循环队列来作为缓冲区;
在循环队列中有两个下标,一个代表生产者的,另一个代表消费者的,生产者生产数据或消费者取出数据都向前移动1;

当队列为空时,那么生产者和消费者的下标都处于相同位置下,需要让生产者先生产,消费者后消费;
队列为满时,生产者和消费者的下标也处于相同的位置,需要让消费者先消费,生产者后生产;
对于其他情况,生产者和消费者的下标都不相同,那么生产者和消费者不就可以同时并发进行了;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • sem_wait函数用于对信号量进行减一操作,如果信号量的值大于零,则将其减一并允许线程(生产者)继续执行。如果信号量的值为零,则线程会被阻塞,直到信号量的值变为非零。这是一个原子操作,确保在任何时刻只有一个线程可以减少信号量的值。所以不用对P函数进一步判断;
  • sem_post函数用于对信号量进行加一操作,这通常用于释放资源或通知等待的线程资源已经可用(消费者从循环队列取出)。当调用sem_post时,如果有一个或多个线程正在等待该信号量,那么其中一个线程将被唤醒并继续执行。

多生产-多消费

当有多个线程时,只要信号量>1,是允许多个线程访问共享资源的而我们这里对于循环队列的生产或者消费都是互斥的,也就是多个生产者时,同时只能有一个生产者对循环队列存入数据,所以这里我们还需要加上互斥锁,使生产者或者消费者有序进入循环队列存取数据;(2把锁,生产者和消费者各一把);
在这里插入图片描述
在这里插入图片描述

代码

Ring_Queue.hpp

#include<iostream>
#include<string>
#include<vector>
#include<semaphore.h>
#include<pthread.h>using namespace std;
template<class T>
class RingQueue
{
public:RingQueue(int cap): _cap(cap),_ring_queue(cap),_productor_step(0),_comsumer_step(0){sem_init(&_room_sem,0,_cap);sem_init(&_data_sem,0,0);pthread_mutex_init(&_productor_mutex,nullptr);pthread_mutex_init(&_consumer_mutex,nullptr);}void Enqueue(const T& in){P(_room_sem);//生产者对空间的判断//如果没有空间,那么将会阻塞于P函数,所以执行下列语句必定有空间Lock(_productor_mutex);_ring_queue[_productor_step++]=in;_productor_step%=_cap;Unlock(_productor_mutex);V(_data_sem);//生产者放入数据进入队列,让消费者的信号量++}void Pop(T* out){P(_data_sem);Lock(_consumer_mutex);*out=_ring_queue[_comsumer_step++];_comsumer_step%=_cap;Unlock(_consumer_mutex);V(_room_sem);}~RingQueue(){sem_destroy(&_room_sem);sem_destroy(&_data_sem);pthread_mutex_destroy(&_productor_mutex);pthread_mutex_destroy(&_consumer_mutex);}
private:void P(sem_t& sem){sem_wait(&sem);}void V(sem_t& sem){sem_post(&sem);}void Lock(pthread_mutex_t &mutex){pthread_mutex_lock(&mutex);}void Unlock(pthread_mutex_t &mutex){pthread_mutex_unlock(&mutex);}//环形队列vector<T> _ring_queue;int _cap;//生产和消费的下标int _productor_step;int _comsumer_step;//定义信号量sem_t _room_sem;//生产者的sem_t _data_sem;//消费者的//定义锁,维护多生产多消费之间的互斥关系pthread_mutex_t _productor_mutex;pthread_mutex_t _consumer_mutex;
};

Thread.hpp

#ifndef __THREAD_HPP__
#define __THREAD_HPP__#include<iostream>
#include<string>
#include<pthread.h>
#include<functional>
#include<unistd.h>using namespace std;namespace ThreadMdule
{template<typename T>using func_t = std::function<void(T&,string name)>;template<typename T>class Thread{public:void Excute(){_func(_data,_threadname);}Thread(func_t<T> func, T& data, const std::string &name="none-name"): _func(func), _data(data), _threadname(name), _stop(true){}static void* threadroutine(void* args){Thread<T>* self=static_cast<Thread<T>*>(args);self->Excute();return nullptr;}bool start(){int n=pthread_create(&_tid,nullptr,threadroutine,this);if(!n){_stop = false;return true;}else{return false;}}void Detach(){if(!_stop){pthread_detach(_tid);}}void Join(){if(!_stop){pthread_join(_tid,nullptr);}}string name(){return _threadname;}void Stop(){_stop = true;}~Thread() {}private:pthread_t _tid;std::string _threadname;T& _data;  func_t<T> _func;bool _stop;};
}#endif

main.cc

#include"RingQueue.hpp"
#include"Thread.hpp"
#include<string>
#include<vector>
#include<unistd.h>
#include"Task.hpp"
#include<ctime>using namespace ThreadMdule;
using ringqueue_t=RingQueue<Task>;
void Productor(ringqueue_t& rq,string name)
{while (true){rq.Enqueue(Download);cout << "Productor : " << name << endl;}
}
void Consumer(ringqueue_t& rq,string name)
{while (true){sleep(1);Task t;rq.Pop(&t);std::cout << "Consumer handler task:  " <<name <<endl;t();//执行任务}
}
void InitComm(std::vector<Thread<ringqueue_t>> *threads, int num, ringqueue_t &rq, func_t<ringqueue_t> func,string who)
{for (int i = 0; i < num; i++){std::string name = "thread-" + std::to_string(i + 1)+"-"+who;threads->emplace_back(func, rq, name);}
}
void InitProductor(vector<Thread<ringqueue_t>>* threads,int num,ringqueue_t& rq)
{InitComm(threads,num,rq,Productor,"productor");
}
void InitConsumer(vector<Thread<ringqueue_t>>* threads,int num,ringqueue_t& rq)
{InitComm(threads,num,rq,Consumer,"consumer");
}void StartAll(vector<Thread<ringqueue_t>>& threads)
{for(auto& thread:threads){std::cout << "start: " << thread.name() << std::endl;thread.start();}
}
void WaitAllThread(std::vector<Thread<ringqueue_t>> &threads)
{for (auto &thread : threads){thread.Join();}
}
int main()
{ringqueue_t* rq=new ringqueue_t(10);vector<Thread<ringqueue_t>> threads;InitProductor(&threads,3,*rq);InitConsumer(&threads,2,*rq);StartAll(threads);WaitAllThread(threads);return 0;
}

e07b649dd8a5e81cbb5234417.png)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/45939.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于PyTorch深度学习实践技术应用

近年来&#xff0c;Python语言由于其开源、简单等特点&#xff0c;受到了广大程序开发者的偏爱&#xff0c;丰富的函数库使得其在各行各业中得到了广泛的应用。伴随着新一轮人工智能&#xff08;尤其是深度学习&#xff09;的快速发展&#xff0c;许多深度学习框架应运而生&…

通义千问Qwen-VL-Chat大模型本地训练(二)

目录 前言 环境准备 软件安装 数据准备 模型训练 模型名称修改 数据集修改 模型参数修改 数据读取编码修改 output_dir修改 模型调用 验证 小结 前言 人工智能大模型是一种能够利用大数据和神经网络来模拟人类思维和创造力的人工智能算法。它利用海量的数据和深度学习技…

中职网络安全B模块Cenots6.8数据库

任务环境说明&#xff1a; ✓ 服务器场景&#xff1a;CentOS6.8&#xff08;开放链接&#xff09; ✓ 用户名&#xff1a;root&#xff1b;密码&#xff1a;123456 进入虚拟机操作系统&#xff1a;CentOS 6.8&#xff0c;登陆数据库&#xff08;用户名&#xff1a;root&#x…

【C++深度探索】全面解析多态性机制(二)

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;C从入门至进阶 这里将会不定期更新有关C/C的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 前言 我…

Debezium日常分享系列之:Debezium 3.0.0.Alpha1 Released

Debezium日常分享系列之&#xff1a;Debezium 3.0.0.Alpha1 Released 一、重大改变Java 和 Maven 要求已更改 二、新的特征和提高MongoDB 三、更多内容 Debezium 3 的第一个预发布版本 3.0.0.Alpha1。这个版本虽然比正常的预版本要小&#xff0c;但高度关注几个关键点&#xff…

《财经日报》︱揭秘随身WiFi市场乱象与格行的破局之路 格行如何树立行业清流新标杆? 随身WiFi真的靠谱吗?

在移动互联网高速发展的今天&#xff0c;随身WiFi以其便捷性和高性价比迅速成为市场宠儿。然而&#xff0c;随着行业的迅速扩张&#xff0c;一系列乱象与套路也逐渐浮出水面&#xff1a;从虚假宣传到限速虚量&#xff0c;随身WiFi行业中的种种套路让消费者防不胜防。商家利用信…

c语言题目之求两个整数的二进制位数的不同个数

文章目录 一、题目二、分析三、代码实现 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、题目 二、分析 首先看到这道题我们是求两个整数的二进制位不同位的个数&#xff0c;在操作符中我们详细学到到了按位操作符相关的内容&#xff0c;首先看到要…

三相感应电机建模仿真(5):考虑铁耗时静止dq坐标系的数学模型及其仿真模型

1.概述 2.考虑铁耗时的三相感应电机数学模型 3.考虑铁耗时的三相感应电机仿真模型 4.仿真实例 5.总结 6.参考文献 1.概述 电机的铁心损耗主要包括涡流损耗和磁滞损耗,这些损耗以热的形式消耗能量,减少了电动机的有效输出功率,对电机效率产生影响;铁心损耗会导致电机内…

【教程】Vue2中使用svg矢量图

1.npm导包 npm i svg-sprite-loader --save2.创建目录放入svg文件&#xff0c;创建SvgIcon.js 3.SvgIcon.js const req require.context(./svg, false, /\.svg$/) const requireAll requireContext > requireContext.keys().map(requireContext) requireAll(req)4.vue.c…

【前端】零基础学会编写CSS

一、什么是CSS CSS (Cascading Style Sheets&#xff0c;层叠样式表&#xff09;是一种是一种用来为结构化文档&#xff08;如 HTML 文档&#xff09;添加样式&#xff08;字体、间距和颜色等&#xff09;的计算机语言&#xff0c;能够对网页中元素位置的排版进行像素级别的精…

【数据结构取经之路】二叉搜索树的实现

目录 前言 二叉搜索树 概念 性质 二叉搜索树的实现 结点的定义 插入 查找 删除 二叉搜索树完整代码 前言 首先&#xff0c;二叉搜索树是一种数据结构&#xff0c;了解二叉搜素树有助于理解map和set的特性。 二叉搜索树 概念 二叉搜索树又称二叉排序树&#xff0c…

【Caffeine】⭐️SpringBoot 项目整合 Caffeine 实现本地缓存

目录 &#x1f378;前言 &#x1f37b;一、Caffeine &#x1f37a;二、项目实践 2.1 环境准备 2.2 项目搭建 2.3 接口测试 ​&#x1f49e;️三、章末 &#x1f378;前言 小伙伴们大家好&#xff0c;缓存是提升系统性能的一个不可或缺的工具&#xff0c;通过缓存可以避免大…

java基础之接口

接口和抽象类很像&#xff0c;接口是把行为给抽象化&#xff0c;可以理解成一个抽象类抽象到极致的情况下&#xff0c;形成的类&#xff0c;也就是一个抽象类有且只有抽象方法的时候&#xff0c;就可以用接口来写。 一、抽象类与接口在书写上的异同 这是一个抽象类 public abst…

五、 计算机网络(考点篇)

1 网络概述和模型 计算机网络是计算机技术与通信技术相结合的产物&#xff0c;它实现了远程通信、远程信息处理和资源共享。计算机网络的功能&#xff1a;数据通信、资源共享、管理集中化、实现分布式处理、负载均衡。 网络性能指标&#xff1a;速率、带宽(频带宽度或传送线路…

什么是人力资源管理审计

企业管理者可以通过会计审计了解公司的财务状况&#xff0c;对企业同样重要的人力状况如何要怎样了解呢&#xff1f;要怎样提高人力资源部门的运行能力&#xff1f;如何实施各种人力资源功能&#xff1f; 相对与财务、会计审计而言&#xff0c;人力资源审计在我国管理层中还是一…

驱动电机液冷冷却系统

1.自然冷却 自然冷却也可以看作是被动散热&#xff0c;它是依靠驱动电机自身的硬件结构&#xff0c;把热量从里经由金属材料向外散热&#xff0c;所以也就不会造成太多的成本支出&#xff0c;但是整体的散热效果并不太好。 考虑到低成本的原因&#xff0c;自然冷却就不能加装…

【简历】重庆某一本大学:JAVA简历指导,中厂通过率较低

注&#xff1a;为保证用户信息安全&#xff0c;姓名和学校等信息已经进行同层次变更&#xff0c;内容部分细节也进行了部分隐藏 简历说明 这是一份重庆某一本大学Java同学的简历。那么因为学校是一个一本的学校&#xff0c;就先要确定就业层次在中厂或者大厂&#xff0c;但是…

串联式 VS 并联式电源连接拓扑

https://download.csdn.net/download/qq_42605300/89538758https://download.csdn.net/download/qq_42605300/89538758串联式电源连接拓扑&#xff1a; 缺点&#xff1a;公共阻抗耦合&#xff0c;引入更多共模干扰。 并联式(星型)电源连接拓扑&#xff1a; 缺点&#xff1a;接地…

【Python】基础语法(顺序语句、条件语句、循环语句)

一、顺序语句 默认情况下&#xff0c;Python 的代码执行顺序是按照从上到下的顺序&#xff0c;依次执行的。 编程是一件明确无歧义的事情&#xff0c;安排好任务的顺序&#xff0c;计算机才能够正确的进行执行。 二、条件语句 1、什么是条件语句 条件语句能够表达 “如果...&…