【Linux】线程安全-信号量

news2025/1/12 21:05:56

文章目录

  • 信号量原理
  • 信号量保证同步和互斥的原理探究
  • 信号量相关函数
    • 初始化信号量函数
    • 等待信号量函数
    • 释放信号量函数
    • 销毁信号量函数
  • 信号量实现生产者消费者模型

信号量原理

信号量的原理:资源计数器 + PCB等待队列 + 函数接口

资源计数器:对共享资源的计数
当执行流获取信号量成功后,信号量当中的计数器减一,如果获取失败,该执行流就会被放入PCB等待队列中
当执行流释放信号量成功之后,信号量当中的计数器会进行加一操作

PCB等待队列:用于存放等待信号量的线程

函数接口:用于操作信号量的一组函数

信号量保证同步和互斥的原理探究

信号量不仅仅可以完成线程之间的同步与互斥,也可以完成进程之间的同步与互斥

互斥原理

1、初始化信号量后,信号量当中的计数器保存的值为1,表示只有一个资源可以被使用

2、当执行流A想要访问共享资源时,首先获取信号量,此时计数器中的值为1,表示可以访问,执行流获取到信号量后,计数器的值从1变成0,执行流A此时去访问共享资源

3、此时,执行流B希望去访问共享资源,首先它要获取信号量,但是信号量中的计数器中的值为0,表示无法获取该信号量,进行无法访问共享资源,因为执行流B的PCB被放进了PCB等待队列中,等待目标信号量的释放,同时,信号量当中的计数器的值进行减一操作,计数器中的值变成了-1,这里的-1表示当前还有1个执行流在等待访问共享资源

同步原理

1、当执行流想要访问共享资源时,首先需要获取信号量

2、如果信号量中的计数器的值大于0,则表示能够获取信号量,进而可以访问共享资源

3、如果信号量中计数器的值小于或等于0,则表示不能获取信号量,进而无法访问共享资源,该执行流被放入PCB等待队列中,同时计数器进行减一操作

4、当释放信号量的时候,会对信号量中计数器进行加一操作

5、如果信号量中的计数器大于0,则唤醒PCB等待队列中的线程

6、如果信号量中的计数器小于或等于0,则不唤醒PCB等待队列中的线程

信号量相关函数

POSIX信号量的函数的名字都是以sem_开头,常用的POSIX信号量函数有以下这些:

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t *sem);

int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, struct timespec*abs_timeout);

sem_t是信号量的类型(sem_t是一个结构体其中有资源计数器和PCB等待队列)

sem_t源码:

typedef union
{
    char __size[__SIZEOF_SEM_T];
    long int __align;
} sem_t;

初始化信号量函数

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

功能:初始化一个信号量

参数:

  • sem:指向被操作的信号量,传入信号量的地址
  • pshared:表示该信号量是用于进程间的还是用于线程间的,填入以下的值
数值含义
0用于线程间,全局变量
非0用于进程间,将信号量所用到的资源在共享内存当中进行开辟

value:资源的个数,本质上就是计数器的值

等待信号量函数

int sem_wait(sem_t *sem);

功能:

执行流调用该函数后,将会对计数器进行减一操作–p操作(P 操作用于获取或锁定信号量),(信号量的计数器自己保证原子性操作不会因为多线程而导致程序计数器中的结果二义性减一操作一步完成。)

如果减一操作后,计数器的值大于0,表示其他执行流仍然可以访问共享资源

如果减一操作后,计数器的值等于0,表示该执行流可以访问共享资源,其他执行流若想访问需要进入PCB等待队列

如果减一操作后,计数器的值小于0,表示当前执行流需要进入PCB等待队列,其他执行流若想访问也需要进入PCB等待队列

参数:sem:指向被操作的信号量,传入信号量的地址

要注意的是,先获取信号量再获取互斥锁,先获取信号量,再保证互斥,就是说,接口一定是先对程序计数器进行减一操作,再拿到锁,假设,如果先拿到锁,再进行信号量减一,那么当拿到锁之后,信号量如果从0减为小于0的数字,那么执行流就会被放到PCB等待队列中去了,这个函数也没有传输互斥锁,所以内部不会进行解锁,所以这时线程就会带着锁进行等待队列,然后无法解锁,锁一直被锁着,其他临界区也无法访问当前临界区资源

释放信号量函数

int sem_post(sem_t *sem);

功能:

执行流调用该函数后,将会对计数器进行加一操作–v操作(V 操作用于释放或解锁信号量)
判断资源计数器的值是否小于等于0

之所以还要判断是否等于0是因为,假设有一个生产者队列和一个消费者队列,当生产者将队列生产满了之后,假设此时程序计数器为-1,而将程序计数器减为-1的那个线程还在等待队列中,此时消费者线程被生产者唤醒,消费数据,出队,它将生产者信号量计数器中加一变成0,那么此时也要通知等待队列中的生产者线程出来工作

是:通知PCB等待队列

否:不用通知PCB等待队列,因为没有线程在等待

参数:sem:指向被操作的信号量,传入信号量的地址

销毁信号量函数

int sem_destroy(sem_t *sem);

功能:销毁目标信号量

参数:sem:指向被操作的信号量,传入信号量的地址

信号量实现生产者消费者模型

代码如下:

#include<pthread.h>
#include<semaphore.h>
#include<stdio.h>
#include<queue>
#include<iostream>
#define CAPACITY 4
#define THREAD_COUNT 2
int g_val = 0;
using namespace std;
class Safe_Queue{
public:
  Safe_Queue(){
    capacity_ = CAPACITY;
    sem_init(&lock_, 0, 1);
    //锁的信号量资源,要么为0表示不可用,要么为1表示可用
    sem_init(&cons_sem_, 0, 0);
    //消费者的信号量,最开始的时候,队列为空,所以消费者没有资源可用
    sem_init(&prod_sem_, 0, capacity_);
    //生产者的信号量,最开始的时候,队列为空,所以生产者可用资源数就是队列大小
  }
  ~Safe_Queue(){
    sem_destroy(&lock_);
    sem_destroy(&prod_sem_);
    sem_destroy(&cons_sem_);
  }
  //插入接口-生产者调用
  void Push(int data){
    sem_wait(&prod_sem_);//等待信号量,执行过后,对计数器进行减一操作
    sem_wait(&lock_);//获取访问_que资源
    _que.push(data);
    printf("I am product, I product %d\n", data);
    //sem_post(&cons_sem_);//对消费者可用的资源计数加一
    sem_post(&lock_);//释放信号量函数,执行过后,对计数器进行加一操作
    sem_post(&cons_sem_);//对消费者可用的资源计数加一
  }
  //获取元素接口-消费者调用
  int Pop(){
    sem_wait(&cons_sem_);
    sem_wait(&lock_);
    int temp = _que.front();
    _que.pop();
    printf("I am consume, I consume %d\n", temp);
    //sem_post(&prod_sem_);
    sem_post(&lock_);
    sem_post(&prod_sem_);
    return temp;
  }
private:
  //STL中的queue是线程不安全的,所以需要进行保护
  queue<int> _que;
  sem_t lock_;//用来保证队列资源互斥的信号量
  sem_t prod_sem_;//生产者的信号量
  sem_t cons_sem_;//消费者的信号量
  size_t capacity_;//人为约定队列的大小
};
void* cons_start(void* arg){
  Safe_Queue *q = (Safe_Queue*)arg;
  while(1){
    q->Pop();
  }
  return NULL;
}
void* prod_start(void* arg){
  Safe_Queue *q = (Safe_Queue*)arg;
  int data = 0;
  while(1){
    q->Push(data);
    data++;
  }
  return NULL;
}
int main(){
  Safe_Queue *q = new Safe_Queue();
  if(q == NULL) return 0;
  pthread_t cons[THREAD_COUNT], prod[THREAD_COUNT];
  for(int i=0; i<THREAD_COUNT; ++i){
    int ret = pthread_create(&prod[i], NULL, prod_start, (void*)q);
    if(ret < 0){
      perror("pthread_create");
      return 0;
    }
    ret = pthread_create(&cons[i], NULL, cons_start, (void*)q);
    if(ret < 0){
      perror("pthread_create");
      return 0;
    }
  }
  for(int i=0; i<THREAD_COUNT; ++i){
    pthread_join(cons[i], NULL);
    pthread_join(prod[i], NULL);
  }
  return 0;
}

执行结果:

在这里插入图片描述

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

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

相关文章

Python之单调栈

单调栈 了解单调栈先要了解栈。栈&#xff08;stack&#xff09;又名堆栈&#xff0c;它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶&#xff0c;相对地&#xff0c;把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈&…

用JAVA(springboot) 开发的彩票模拟系统

闲暇时间&#xff0c;自己写了一个模拟彩票系统&#xff0c;里面研究了开奖算法&#xff0c;下单算法&#xff0c;彩票的各种计算规则。需要源码的私信&#xff0c;研究为主&#xff0c;切勿商务用途。

2022年06月 C/C++(七级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C编程&#xff08;1~8级&#xff09;全部真题・点这里 第1题&#xff1a;有多少种二叉树 输入n(1<n<13)&#xff0c;求n个结点的二叉树有多少种形态 时间限制&#xff1a;1000 内存限制&#xff1a;65536 输入 整数n 输出 答案 样例输入 3 样例输出 5 这个问题可以使用…

外部中断(EXTI) - 按键控制LED

一、外部中断/事件控制器(EXTI)结构图 1、结构图分析 外部中断主要由外部中断/事件控制器(External interrupt/event controller, EXTI)控制&#xff0c;它管理了外部中断或者事件的使能与否、触发方式等功能。 &#xff08; 外部中断/事件控制器(EXTI)结构图 &#xff09; …

记录错误:Access denied for user ‘root‘@‘localhost‘ (using password:No) 解决方案

他说我没输入密码&#xff0c;但是我输入了啊&#xff1f;&#xff1f;于是&#xff0c;我试了试这儿&#xff0c;password 一改就好了。。。 他原来是是我打的很快&#xff0c;快速生成的。。。。

ESP32之LEDC(PWM信号的输出)

一、PWM信号简介 PWM&#xff1a;脉冲宽度调制&#xff0c;简称脉宽调制频率(f)&#xff1a;一秒钟PWM有多少个周期(单位Hz)周期(T)&#xff1a;一个周期的时间占空比(duty)&#xff1a;在一个脉冲周期内&#xff0c;高电平的时间与整个周期时间的比例脉宽时间&#xff1a;一个…

React 中的 ref 如何操作 dom节点,使输入框获取焦点

聚焦文字输入框 .focus() 获取焦点 当用户点击按钮时&#xff0c;handleClick 函数会被调用&#xff0c;从而将焦点聚焦到文本输入框上。 // 焦文字输入框 import { useRef } from "react";const FocusForm () > {const inputRef useRef<any>(null);func…

C到C++的升级

C和C的关系 C继承了所有C语言的特性&#xff1b;C在C的基础上提供了更多的语法和特性&#xff0c;C语言去除了一些C语言的不好的特性。C的设计目标是运行效率与开发效率的统一。 变化一&#xff1a;所有变量都可以在使用时定义 C中更强调语言的实用性&#xff0c;所有的变量…

内网隧道代理技术(二十二)之 CS针对特定端口上线不出网机器

CS工具自带上线不出网机器 如图A区域存在一台中转机器,这台机器可以出网,这种是最常见的情况。我们在渗透测试的过程中经常是拿下一台边缘机器,其有多块网卡,边缘机器可以访问内网机器,内网机器都不出网。这种情况下拿这个边缘机器做中转,就可以使用CS工具自带上线不出网…

代码随想录Day_53打卡

①、最长公共子序列 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符…

Flutter状态管理 — 探索Flutter中的状态

前言 随着响应式编程的理念&Flutter被大众所了解以来&#xff0c;状态管理一直是一个引人深思的话题。如果想要学习好Flutter这样的响应式的编程框架就一定是离不开状态管理的。我遇到过很多没有了解过响应式编程框架的&#xff0c;或者从事后端开发&#xff0c;自己想用F…

jmeter While控制器

一种常见的循环控制语句&#xff0c;用于重复执行一段代码块&#xff0c;直到指定的条件不再满足。 参数&#xff1a; 空LASTJMeter变量、函数、属性或任意其他可用表达式 &#xff08;jmeter提供的方法&#xff09;。判断变量值count_num小于等于20&#xff0c;推荐简单的几…

Python入门教程 | Python3 元组(tuple)

创建元组 Python 的元组与列表类似&#xff0c;不同之处在于元组的元素不能修改。 元组使用小括号 ( )&#xff0c;列表使用方括号 [ ]。 元组创建很简单&#xff0c;只需要在括号中添加元素&#xff0c;并使用逗号隔开即可。 >>> tup1 (Google, Tarzan, 1997, …

组相联cache如何快速实现cache line eviction并使用PMU events验证

如何快速实现cache line eviction 一&#xff0c;什么是cache hit、miss、linefill、evict &#xff1f;1.1 如果要程序员分别制造出cache hit、miss、linefill、evict这四种场景&#xff0c;该怎么做&#xff1f; 二&#xff0c;实现cache line eviction的方法1.1 直接填充法3…

STM32WB55开发(1)----监测STM32WB连接状态

STM32WB55开发----1.监测STM32WB连接状态 概述硬件准备视频教学样品申请选择芯片型号配置时钟源配置时钟树RTC时钟配置查看开启STM32_WPAN条件配置HSEM配置IPCC配置RTC启动RF开启蓝牙LED配置设置工程信息工程文件设置参考文档SVCCTL_App_Notification结果演示 概述 STM32WB系列…

useRef 定义的 ref 在控制台可以打印但是页面不生效?

useRef 是一个 React Hook&#xff0c;它能让你引用一个不需要渲染的值。 点击计时器 点击按钮后在控制台可以打印但是页面不生效。 useRef 返回的值在函数组件中不会自动触发重新渲染&#xff0c;所以控制台可以显示变化而按钮上无法显示 ref.current的变化。 import { use…

ConcurrentHashMap集合

什么是ConcurrentHashMap&#xff1f; ConcurrentHashMap 和HashMap一样&#xff0c;是一个存放键值对的容器。使用Hash算法来获取值的地址&#xff0c;因此时间复杂度是O(1)。查询非常快。ConcurrentHashMap 同时也是线程安全版的HashMap&#xff0c;可以实现线程安全的集合的…

文献阅读:Chain-of-Thought Prompting Elicits Reasoning in Large Language Models

文献阅读&#xff1a;Chain-of-Thought Prompting Elicits Reasoning in Large Language Models 1. 文章简介2. 具体方法3. 实验结果 1. 数学推理 1. 实验设计2. 实验结果3. 消解实验4. 鲁棒性考察 2. 常识推理 1. 实验设计2. 实验结果 3. 符号推理 1. 实验设计2. 实验结果 4.…

融合MMEdu和Transformers技术的视障出行智能辅助系统(上海浦育AI未来夏令营结题论文)

融合MMEdu和Transformers技术的视障出行智能辅助系统 摘要 面对社会生活中众多视障者对出行的需求&#xff0c;视障出行智能辅助系统融合MMEdu和Transformers技术为视障者提供实时路况分析。本系统利用图像分类、目标检测和深度估计等软件技术&#xff0c;对摄像头实时获取的每…

MATLAB中编译器中的变量联系到Simulink

MATLAB中编译器中的变量联系到Simulink 现在编译器中创建变量&#xff0c;进行编译&#xff0c;使其生成在工作区。 然后在Simulink中国使用变量即可。