<Linux>POSIX信号量

news2024/12/23 10:06:54

 

目录

 什么是信号量

如何理解信号量的使用

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

如上问题我们如何用编码保证 ?(信号量)

编码:


POSIX信号量和SystemV信号量作用相同,都是用于同步操作。POSIX可以用于线程同步。

信号量本质上就是一个计数器。

 什么是信号量

只要保证共享资源任何时刻都只有一个执行流在进行访问,就有了临界资源和临界区的概念。互斥(加锁)时候,共享资源是被当做整体使用的。如果不同线程访问的是共享资源中的不同的位置,这个共享资源就可以分开来使用,让不同的执行流访问不同的区域,就可以继续并发了。

那么我们如何知道这个一共有多少个资源,还剩多少个?自己初始化的

你怎么保证这个资源就是给你的?程序员编码

我怎么知道我一定可以具有一个共享资源呢?信号量

电影院的例子:

买票的本质:叫做资源(座位)的预定机制。

信号量本质:是一个计数器,访问临界资源的时候,必须先申请信号量资源(sem--, 预定资源, P),使用完毕信号量资源(sem++, 释放资源,V)。

如何理解信号量的使用

我们申请了一个信号量,代表我当前执行流一定具有一个资源,可以被它使用了,那么是哪一个资源呢?需要程序员根据场景自己编码完成!

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

项目:就是 物理结构->逻辑结构,在加一些条件循环判断。看项目时,看核心的物理结构代码,联想到逻辑结构。

环形队列:

判空:start == end

判满:1.计数器 2.浪费一个格子

空的时候,消费者线程等待;满的时候,生产者线程等待。

 如果生产和消费指向了环形的同一个位置(表示队列为空or未满):生产和消费要有互斥或者同步问题。但是大概率生产线程和消费线程指向的是不同的位置,所以只需要让生产和消费指向同一个位置,具有互斥同步关系就可以了,而让生产和消费不指向同一个位置时,并发指向!

我们的期望:生产者和消费者套圈(数据覆盖问题)、消费者不能超过生产者,指向同一个位置的情况:1.为空时,让生产者先运行;2.为满,让消费者先运行;其他情况可以并发访问。

如上问题我们如何用编码保证 ?(信号量)

先看看两者关注什么

生产者:最关注的是空间资源 -> spaceSem信号量 -> N

消费者:最关注的是数据资源 -> dataSem信号量 -> 0

进行生产:先申请信号量 P(spaceSem) -> spaceSem--,然后再特定位置生产。V(dataSem)

进行消费:先申请信号量P(dataSem) -> dataSem--, 然后消费特定的数据。V(spaceSem)

信号量申请失败,线程被挂起。

问题:如何保证在特定的位置访问自己的资源? 两者线程都有自己的下标即可。

编码:

#include "ringQueue.hpp"
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <ctime>

int myAdd(int x, int y)
{
    return  x + y;
}

void* consume(void* args)
{
    RingQueue<Task>* rq = (RingQueue<Task>*)args;

    while (true)
    {
        Task t;
        rq->pop(&t);
        std::cout << "线程 " << pthread_self() << " 消费成功:" << "=" << t() << std::endl;
    }
}

void* product(void* args)
{
    RingQueue<Task>* rq = (RingQueue<Task>*)args;
    while (true)
    {
        int x = rand() % 100;
        int y = rand() % 100;
        Task t(x, y, myAdd);
        rq->push(t);
        std::cout << "线程 " << pthread_self() << " 生产任务成功:" << x << "+" << y << "=?" << std::endl;
    }
}

int main()
{
    srand((unsigned int)time(nullptr));

    pthread_t c[3], p[5];
    RingQueue<int> *rq = new RingQueue<int>();

    for (int i = 0; i < 3; ++i) pthread_create(c + i, nullptr, consume, rq);
    for (int i = 0; i < 5; ++i) pthread_create(p, nullptr, product, rq);

    for (int i = 0; i < 3; ++i) pthread_join(c[i], nullptr);
    for (int i = 0; i < 5; ++i) pthread_join(p[i], nullptr);

    return 0;
}
#pragma once

#include "sem.hpp"
#include "Task.hpp"
#include <iostream>
#include <vector>
#include <pthread.h>

const int g_data_num = 5;

// 定义环形队列
template<class T>
class RingQueue
{
public:

    RingQueue(int num = g_data_num)
    :_num(num)
    ,_ring_queue(num)
    ,_c_step(0)
    ,_p_step(0)
    ,_space_sem(num)
    ,_data_sem(0)
    {
        pthread_mutex_init(&_c_mtx, nullptr);
        pthread_mutex_init(&_p_mtx, nullptr);
    }

    void push(T& in)
    {
        // 先预空间申请信号量
        _space_sem.p();
        // 在进行实际处理
        // std::cout << "空间申请成功" << std::endl;
        pthread_mutex_lock(&_p_mtx);
        _ring_queue[_p_step++] = in;
        _p_step %= _num;

        // std::cout << "线程 " << pthread_self() << " 生产任务成功:" << in << std::endl;
        pthread_mutex_unlock(&_p_mtx);
        _data_sem.v();

    }

    void pop(T* out)
    {
        // 先预数据申请信号量
        _data_sem.p();
        // 在进行实际处理
        // std::cout << "数据申请成功" << std::endl;
        pthread_mutex_lock(&_c_mtx);
        *out = _ring_queue[_c_step++];
        _c_step %= _num;

        // std::cout << "线程 " << pthread_self() << " 任务消费成功:" << *out << std::endl;
        pthread_mutex_unlock(&_c_mtx);
        _space_sem.v();
    }

    ~RingQueue()
    {
        pthread_mutex_destroy(&_c_mtx);
        pthread_mutex_destroy(&_p_mtx);
    }
private:
    std::vector<T> _ring_queue; // 队列
    int _num; // 数据最大个数
    int _c_step; // 消费者下标
    int _p_step; // 生产者下标
    Sem _space_sem; // 空间信号量
    Sem _data_sem; // 数据信号量
    pthread_mutex_t _c_mtx; // 消费者锁
    pthread_mutex_t _p_mtx; // 生产者锁
};
#ifndef __TASK_HPP
#define __TASK_HPP

#include <iostream>
#include <functional>

// typedef std::function<int (int, int)> func_t;
typedef int(*func_t)(int, int);

class Task
{
public:
    Task()
    {}

    Task(int x, int y, func_t func)
    :_x(x)
    ,_y(y)
    ,_func(func)
    {

    }

    int operator()()
    {
        return  _func(_x, _y);
    }

private:
    int _x;
    int _y;
    func_t _func;
};

#endif
#ifndef __TASK_HPP
#define __TASK_HPP

#include <iostream>
#include <functional>

// typedef std::function<int (int, int)> func_t;
typedef int(*func_t)(int, int);

class Task
{
public:
    Task()
    {}

    Task(int x, int y, func_t func)
    :_x(x)
    ,_y(y)
    ,_func(func)
    {

    }

    int operator()()
    {
        return  _func(_x, _y);
    }

private:
    int _x;
    int _y;
    func_t _func;
};

#endif
ring_queue:testMain.cc
	g++ -o $@ $^ -std=c++11 -lpthread 
.PHONY:clean
clean:
	rm -f ring_queue

多生产多消费的意义? 不要狭隘的认为,把认为或者数据放在交易场所,就是生产和消费,将数据或者任务生产前和那道之后的处理,才是最耗费时间的。

生产的本质:私有的任务->公共空间

消费的本质:公共空间中的认为->私有的

信号量的本质是一个计数器,计数器的意义是什么?可以不用进入临界区,就可以得知资源情况,甚至可以减少临界区内部的判断。

以前申请锁 -> 判断与访问 -> 释放锁 --- 这么做的本质是我们并不清楚临界资源的情况!!信号量要提前预设资源的情况,而且在pv变化过程中,我们可以在外部就能知晓临界资源的情况!

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

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

相关文章

TS数据类型

基本数据类型 null undefined number string boolean 对于基本数据类型&#xff0c;可以不写类型注解&#xff0c;ts能通过值来判断变量的类型 let nu null let un undefined let num 23 let str "sdfk" let isShow true引用数据类型 数组 写法1 let arr:…

iconik--AI智能媒体管理解决方案

ftrack于去年加入Backlight&#xff0c;旗下有Celtx, Iconik, Wildmoka, 和Zype。这些公司都为媒体、娱乐和视频领域的客户提供基于云的解决方案。 今天&#xff0c;我们就来隆重地介绍其中一款软件–iconik&#xff01;谷歌、VICE媒体、亚马逊旗下米高梅、Complex Networks和S…

【C++初阶】C++入门

⭐博客主页&#xff1a;️CS semi主页 ⭐欢迎关注&#xff1a;点赞收藏留言 ⭐系列专栏&#xff1a;C初阶 ⭐代码仓库&#xff1a;C初阶 家人们更新不易&#xff0c;你们的点赞和关注对我而言十分重要&#xff0c;友友们麻烦多多点赞&#xff0b;关注&#xff0c;你们的支持是我…

GMW协议

概述 回顾混淆电路的流程&#xff0c;一方生成加密真值表&#xff0c;另一方执行计算&#xff0c;门电路的输入通过主动发送和不经意传输索取实现&#xff0c;用这样的方式来达到多方计算中一些公平性。 那么是否可以让双方拥有更加对等的地位&#xff0c;让每个参与方都持有一…

华为OD机试真题(Java),数组合并(100%通过+复盘思路)

一、题目描述 现在有多组整数数组&#xff0c;需要将他们合并成一个新的数组。 合并规则从每个数组里按顺序取出固定长度的内容&#xff0c;合并到新的数组&#xff0c;取完的内容会删除掉。 如果改行不足固定长度&#xff0c;或者已经为空&#xff0c;则直接取出剩余部分的内…

Numpy从入门到精通——Numpy运算符|批处理

这个专栏名为《Numpy从入门到精通》&#xff0c;顾名思义&#xff0c;是记录自己学习numpy的学习过程&#xff0c;也方便自己之后复盘&#xff01;为深度学习的进一步学习奠定基础&#xff01;希望能给大家带来帮助&#xff0c;爱睡觉的咋祝您生活愉快&#xff01; 这一篇介绍《…

Android 项目编译 Gradle 配置说明

前言 Android 的Gradle版本更新换代还是很快的&#xff0c;更新换代除了功能上变得强大之外&#xff0c;还会出现很多意料之外的Bug&#xff0c;而很多开发者会被折磨的死去活来&#xff0c;下面我们来看有哪些编译配置。 正文 首先要知道什么时候会进行编译&#xff0c;有以下…

Efficient Attention: Attention with Linear Complexities

paper: https://arxiv.org/pdf/1812.01243.pdf 这里写目录标题 一、引言二、方法实现高效注意力的解释效率优势 三、实验消融插入层键的维度骨干架构 一、引言 注意机制在计算机视觉和自然语言处理中有着广泛的应用。最近的工作开发了点积注意力机制&#xff0c;并将其应用于…

MobileBERT模型简单介绍

目录 一、概要 二、深入扩展 2.1 知识蒸馏方法 2.2 渐进式知识迁移 一、概要 MobileBERT 可以看作一个“瘦身”后的BERT-large模型&#xff0c;其使用了瓶颈结构&#xff08;Bottleneck Structure&#xff09;&#xff0c;并且在自注意力和前馈神经网络的设计上也有一定的改…

图形化之家谱遗传系统

1&#xff1a;废话不多说先看成果。 QQ录屏20230418163603 QQ录屏20230418163732 2&#xff1a;解析&#xff1a; 1&#xff1a;不知道会有多少个孩子&#xff0c;所以我们用二叉树的孩子兄弟结构 typedef struct treeNode {char name[100];//名字int generation;//辈分char g…

Pytorch深度学习笔记(五)反向传播算法

推荐课程&#xff1a;04.反向传播_哔哩哔哩_bilibili 1.为什么要使用反向传播算法 简单模型可以使用解析式更新w 复杂模型&#xff0c;如图&#xff0c;输入矩阵为5*1矩阵&#xff0c;等一层权重矩阵H1为6*5矩阵&#xff0c;则需要30个解析式&#xff0c;第二层权重矩阵H2为6…

1685_Excel的几种脚本处理方式

全部学习汇总&#xff1a; GreyZhang/python_basic: My learning notes about python. (github.com) 做个小结&#xff0c;实际上是写的我自己学习的过程。 关于Excel的处理方式很多&#xff0c;我也不会那么多&#xff0c;在这里我只想写一下我自己接触过的。大致是三种方式&a…

Pikachu靶场(Cross-Site Scripting)

Cross-Site Scripting 反射型xss(get)源代码修改限制地址栏 反射性xss(post)存储型xssDOM型xss-xxss盲打xss之过滤xss之htmlspecialcharsxss之href输出xss之js输出 Cross-Site Scripting 简称为“CSS”&#xff0c;为避免与前端叠成样式表的缩写"CSS"冲突&#xff0c…

《花雕学AI》25:用文字画出你的非凡想象力,微软新Bing带你体验DALL-E的神奇

你有没有想过用文字来画画&#xff1f;这听起来可能很不可思议&#xff0c;但是现在&#xff0c;你可以通过微软新Bing来实现这个想法。微软新Bing支持AI绘画功能&#xff0c;只要输入一句话&#xff0c;就能生成一幅图像。这个功能是由DALL-E驱动的&#xff0c;DALL-E是一个能…

mybatis03-多表查询、延迟加载、逆向工程

mybatis03 mybatis 多表联查 背景产生&#xff1a;开发过程中单表查询 不能满足项目需求分析功能。对于复杂业务来说&#xff0c;关联的表有几张&#xff0c;甚至几十张 并且表与表之间的关系相当复杂。目的&#xff1a;实现复杂业务功能&#xff0c;必须进行多表查询&#x…

开发插件JFormDesigner(可视化GUI编程)的使用与注册-简单几步即可完成

开发插件JFormDesigner&#xff08;可视化GUI编程&#xff09;的使用与注册 获取链接&#xff1a;1.JFormDesigner获取2.记录插件下载路径3.使用zcj注册4.生成license5.打开idea进行注册 获取链接&#xff1a; https://pan.baidu.com/s/1N9ua2p3BpiMIARCEewRxIw?pwd4e9a 提取…

WebSocket 通信 —— 浏览器原生支持

在上一篇内容中讲到使用Node中的Net核心模块完成socket通信&#xff0c;那么本篇就继续来讲关于浏览器原生支持的 WebSocket &#xff0c;实现通信。那么什么是 WebSocket ? 它是HTML5开始提供的一种浏览器与服务器间进行全双工&#xff08;全双工&#xff1a;同时进行双向传输…

激活函数(Activation Function)及十大常见激活函数

目录 1 激活函数的概念和作用 1.1 激活函数的概念 1.2 激活函数的作用 1.3 通俗地理解一下激活函数&#xff08;图文结合&#xff09; 1.3.1 无激活函数的神经网络 1.3.2 带激活函数的神经网络 2 神经网络梯度消失与梯度爆炸 2.1 简介梯度消失与梯度爆炸 2.2 梯度不稳…

asp.net+C#房地产销售系统文献综述和开题报告+Lw

本系统使用了B/S模式&#xff0c;使用ASP.NET语言和SQL Server来设计开发的。首先把所有人分为了用户和管理员2个部分&#xff0c;一般的用户可以对系统的前台进行访问&#xff0c;对一般的信息进行查看&#xff0c;而注册用户就可以通过登录来完成对房屋信息的查看和对房屋的…

动态通讯录——C语言【详解+全部码源】

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a;进阶C语言&#xff0c;本专栏主要讲解数据存储&#xff0c;进阶指针&#xff0c;动态内存管理&a…