【Linux-多线程】POSIX信号量-基于环形队列生产消费模型

POSIX信号量

POSIX信号量和System V信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源的目的。但POSIX可以用于线程间同步

1.快速认识信号量接口

POSIX信号量分为两种类型:

  1. 命名信号量(Named Semaphores):命名信号量可以在多个进程之间共享,因为它们通过一个名字来标识,这个名字在文件系统中可见。

  2. 未命名信号量(Unnamed Semaphores):未命名信号量通常用于同一进程内的线程同步,因为它们没有名字,只能通过传递它们在内存中的地址来进行共享。

以下是一些常用的POSIX信号量接口函数:

初始化信号量
  • sem_t sem;:声明一个信号量变量。

  • int sem_init(sem_t *sem, int pshared, unsigned int value);:初始化一个未命名的信号量。pshared参数如果为0,则信号量在进程内线程间共享;如果非0,则信号量在进程间共享。value参数是信号量的初始值。

销毁信号量
  • int sem_destroy(sem_t *sem);:销毁一个之前由sem_init初始化的未命名信号量。

操作信号量
  • int sem_wait(sem_t *sem);:等待信号量变为正值,然后将其减一。如果信号量的值为0,则调用线程会被阻塞,直到信号量变为正值。

  • int sem_trywait(sem_t *sem);:尝试等待信号量,如果信号量的值为0,则立即返回EAGAIN错误,不会阻塞。

  • int sem_post(sem_t *sem);:增加信号量的值。如果其他线程正在等待该信号量,它们中的一个可能会被唤醒。

  • int sem_getvalue(sem_t *sem, int *sval);:获取信号量的当前值。

命名信号量的操作
  • sem_t *sem_open(const char *name, int oflag, ...);:打开一个命名信号量。name是信号量的名字,oflag是打开标志,可以是O_CREAT等。

  • int sem_close(sem_t *sem);:关闭之前打开的命名信号量。

  • int sem_unlink(const char *name);:删除命名信号量的名字,允许它在不再被任何进程引用时释放资源。

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

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

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

问题:多线程如何在环形队列中进行生产和消费 ?

1.如果队列为空,生产者先生产

2.如果队列满了,消费者来消费

3.如果队列不为空,也不为满,让生产和消费同时进行

注意:

a.不让生产者把消费者套一个圈

b.不能让消费者,超过生产者

用信号量就能做到如果队列为空,生产者先生产;如果队列满了,消费者来消费

消费者需要管理数据资源,生产者需要管理空间资源

所以数据资源+空间资源 = N

初始化的时候 空间资源space_sem = 0;数据资源 data_sem = N

在生产者这里:需要P(space_sem)(--空间资源) V(data_sem)(++数据资源)

在消费者这里:需要P(space_sem) (--数据资源) V(space_sem)(++空间资源)

Task.hpp
#pragma once

#include<iostream>
#include<functional>

// typedef std::function<void()> task_t;
// using task_t = std::function<void()>;

// void Download()
// {
//     std::cout << "我是一个下载的任务" << std::endl;
// }


// 要做加法
class Task
{
public:
    Task()
    {
    }
    Task(int x, int y) : _x(x), _y(y)
    {
    }
    void Excute()
    {
        _result = _x + _y;
    }
    void operator ()()
    {
        Excute();
    }
    std::string debug()
    {
        std::string msg = std::to_string(_x) + "+" + std::to_string(_y) + "=?";
        return msg;
    }
    std::string result()
    {
        std::string msg = std::to_string(_x) + "+" + std::to_string(_y) + "=" + std::to_string(_result);
        return msg;
    }

private:
    int _x;
    int _y;
    int _result;
};
RingQueue.hpp
#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <pthread.h>
#include <semaphore.h>

template <class T>
class RingQueue
{
private:
    void P(sem_t &s)
    {
        sem_wait(&s);
    }
    void V(sem_t &s)
    {
        sem_post(&s);
    }

public:
    RingQueue(int max_cap)
        : _ringqueue(max_cap), _max_cap(max_cap), _c_step(0), _p_step(0)
    {
        sem_init(&_data_sem, 0, 0);
        sem_init(&_space_sem, 0, max_cap);

        pthread_mutex_init(&_c_mutex, nullptr);
        pthread_mutex_init(&_p_mutex, nullptr);
    }

    void Push(const T &in) // 生产者
    {
        // 信号量:是一个计数器,是资源的预订机制。
        // 预订:在外部,可以不判断资源是否满足,就可以知道内部资源的情况!
        P(_space_sem); // 信号量这里,对资源进行使用,申请,为什么不判断一下条件是否满足???信号量本身就是判断条件!
        pthread_mutex_lock(&_p_mutex);
        _ringqueue[_p_step++] = in;
        _p_step %= _max_cap;
        pthread_mutex_unlock(&_p_mutex);
        V(_data_sem);
    }

    void Pop(T *out) // 消费
    {
        P(_data_sem);
        pthread_mutex_lock(&_c_mutex);
        *out = _ringqueue[_c_step++];
        _c_step %= _max_cap;
        pthread_mutex_unlock(&_c_mutex);
        V(_space_sem);
    }

    ~RingQueue()
    {
        sem_destroy(&_data_sem);
        sem_destroy(&_space_sem);

        pthread_mutex_destroy(&_c_mutex);
        pthread_mutex_destroy(&_p_mutex);
    }

private:
    std::vector<T> _ringqueue;
    int _max_cap;

    int _c_step;
    int _p_step;

    sem_t _data_sem;  // 消费者关心
    sem_t _space_sem; // 生产者关心

    pthread_mutex_t _c_mutex;
    pthread_mutex_t _p_mutex;
};
main.cc
#include "RingQueue.hpp"
#include "Task.hpp"
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <ctime>

void *Consumer(void *args)
{
    RingQueue<Task> *rq = static_cast<RingQueue<Task> *>(args);
    while (true)
    {
        Task t;
        // 1. 消费
        rq->Pop(&t);

        // 2. 处理数据
        t();
        std::cout << "Consumer-> " << t.result() << std::endl;
    }
}

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

    while (true)
    {
        sleep(1);

        // 1. 构造数据
        int x = rand() % 10 + 1; //[1, 10]
        usleep(x * 1000);
        int y = rand() % 10 + 1;
        Task t(x, y);

        // 2. 生产
        rq->Push(t);

        std::cout << "Productor -> " << t.debug() << std::endl;
    }
}

int main()
{
    RingQueue<Task> *rq = new RingQueue<Task>(5);
    // 单单
    pthread_t c1, c2, p1, p2, p3;
    pthread_create(&c1, nullptr, Consumer, rq);
    pthread_create(&c2, nullptr, Consumer, rq);
    pthread_create(&p1, nullptr, Productor, rq);
    pthread_create(&p2, nullptr, Productor, rq);
    pthread_create(&p3, nullptr, Productor, rq);

    pthread_join(c1, nullptr);
    pthread_join(c2, nullptr);
    pthread_join(p1, nullptr);
    pthread_join(p2, nullptr);
    pthread_join(p3, nullptr);

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux下文件操作相关接口

文章目录 一 文件是什么普通数据文件 二 文件是谁打开的进程用户 三 进程打开文件的相关的接口c语言标准库相关文件接口1. fopen 函数2. fread 函数3. fwrite 函数4. fclose 函数5. fseek 函数 linux系统调用接口1. open 系统调用2. creat 系统调用3. read 系统调用4. write 系…

UE蓝图节点备忘录

获取索引为0的玩家 获取视图缩放 反投影屏幕到世界 获取屏幕上的鼠标位置 对指定的物体类型进行射线检测 判断物体是否有实现某个接口 上面节点的完整应用 通过PlayerControlle获取相机相关数据 从相机处发射射线撞击物体从而获取物体信息 抽屉推拉功能 节点说明 ##门的旋转开关…

玩机搞机基本常识-------列举安卓机型一些不常用的adb联机命令

前面分享过很多 常用的adb命令&#xff0c;今天分享一些不经常使用的adb指令。以作备用 1---查看当前手机所有app包名 adb shell pm list package 2--查看当前机型所有apk包安装位置 adb shell pm list package -f 3--- 清除指定应用程序数据【例如清除浏览器应用的数据】 …

LeetCode【剑指offer】系列(字符串篇)

剑指offer05.替换空格 题目链接 题目&#xff1a;假定一段路径记作字符串path&#xff0c;其中以 “.” 作为分隔符。现需将路径加密&#xff0c;加密方法为将path中的分隔符替换为空格" "&#xff0c;请返回加密后的字符串。 思路&#xff1a;遍历即可。 通过代…

idea java.lang.OutOfMemoryError: GC overhead limit exceeded

Idea build项目直接报错 java: GC overhead limit exceeded java.lang.OutOfMemoryError: GC overhead limit exceeded 设置 编译器 原先heap size 设置的是 700M , 改成 2048M即可

aws(学习笔记第二十二课) 复杂的lambda应用程序(python zip打包)

aws(学习笔记第二十二课) 开发复杂的lambda应用程序(python的zip包) 学习内容&#xff1a; 练习使用CloudShell开发复杂lambda应用程序(python) 1. 练习使用CloudShell CloudShell使用背景 复杂的python的lambda程序会有许多依赖的包&#xff0c;如果不提前准备好这些python的…

conda 批量安装requirements.txt文件

conda 批量安装requirements.txt文件中包含的组件依赖 conda install --yes --file requirements.txt #这种执行方式&#xff0c;一遇到安装不上就整体停止不会继续下面的包安装。 下面这条命令能解决上面出现的不执行后续包的问题&#xff0c;需要在CMD窗口执行&#xff1a; 点…

如何操作github,gitee,gitcode三个git平台建立镜像仓库机制,这样便于维护项目只需要维护一个平台仓库地址的即可-优雅草央千澈

如何操作github&#xff0c;gitee&#xff0c;gitcode三个git平台建立镜像仓库机制&#xff0c;这样便于维护项目只需要维护一个平台仓库地址的即可-优雅草央千澈 问题背景 由于我司最早期19年使用的是gitee&#xff0c;因此大部分仓库都在gitee有几百个库的代码&#xff0c;…

SpringBootWeb 登录认证(day12)

登录功能 基本信息 请求参数 参数格式&#xff1a;application/json 请求数据样例&#xff1a; 响应数据 参数格式&#xff1a;application/json 响应数据样例&#xff1a; Slf4j RestController public class LoginController {Autowiredpriva…

nginx http反向代理

系统&#xff1a;Ubuntu_24.0.4 1、安装nginx sudo apt-get update sudo apt-get install nginx sudo systemctl start nginx 2、配置nginx.conf文件 /etc/nginx/nginx.conf&#xff0c;但可以在 /etc/nginx/sites-available/ 目录下创建一个新的配置文件&#xff0c;并在…

【25考研】川大计算机复试情况,重点是啥?怎么准备?

24年进入复试的同学中&#xff0c;有10位同学的复试成绩为0分。具体是个人原因还是校方原因&#xff0c;还尚不明确。但是C哥提醒&#xff0c;一定要认真复习&#xff01;复试完后不要跟任何人讨论有关复试的题目及细节&#xff01; 一、复试内容 四川大学复试内容较多&#xf…

React Native 项目 Error: EMFILE: too many open files, watch

硬件&#xff1a;MacBook Pro (Retina, 13-inch, Mid 2014) OS版本&#xff1a;MacOS BigSur 11.7.10 (20G1427) 更新: 删除modules的方法会有反弹&#xff0c;最后还是手动安装了预编译版本的watchman。 React Native 项目运行npm run web&#xff0c;出现如下错误&#xff1a…

YOLO11改进算法 | 引入SimAM模块的YOLO11-pose关键点姿态估计

目录 网络结构 测试结果 算法改进 局部和全局特征的兼顾 提升模型精度 提高计算效率 增强模型鲁棒性 模型指标 数据集介绍 Coovally AI模型训练与应用平台 YOLO11是由Ultralytics团队于2024年9月30日发布的&#xff0c;它是YOLO&#xff08;You Only Look Once&#…

运放输入偏置电流详解

1 输入阻抗与输入偏置电路关系 在选择运放和仪表运放时&#xff0c;经常听到这样的说法&#xff1a;“需要非常高的输入阻抗”&#xff0c;事实上真实如此吗&#xff1f; 输入阻抗&#xff08;更确切的说是输入电阻&#xff09;很少会成为一个重要的问题&#xff08;输入电容也…

HTML基础入门——简单网页页面

目录 一&#xff0c;网上转账电子账单 ​编辑 1&#xff0c;所利用到的标签 2&#xff0c;代码编写 3&#xff0c;运行结果 二&#xff0c;李白诗词 1&#xff0c;所用到的标签 2&#xff0c;照片的编辑 3&#xff0c;代码编写 4&#xff0c;运行结果 一&#xff0c;网…

Kubernetes集群架构

Kubernetes集群架构 Kubernetes 集群架构控制平面组件kube-apiserveretcdkube-schedulerkube-controller-managercloud-controller-manager 节点组件kubeletkebe-proxy&#xff08;可选&#xff09;容器运行时 插件DNSWeb UI&#xff08;Dashboard&#xff09;容器资源监控集群…

腾讯云AI代码助手-每日清单助手

作品简介 每日清单助手是一款可以记录生活的小程序&#xff0c;在人们需要记录时使用&#xff0c;所以根据这个需求来创建的这款应用工具&#xff0c;使用的是腾讯云AI代码助手来生成的所有代码&#xff0c;使用方便&#xff0c;快捷&#xff0c;高效。 技术架构 python语言…

创建基本的 Electron 应用项目的详细步骤

创建一个基本的 Electron 应用项目的详细步骤。我们将从安装 Node.js 开始&#xff0c;然后创建项目文件夹并初始化 Electron 项目。 1. 安装 Node.js 首先&#xff0c;确保你已经安装了 Node.js 和 npm。你可以在终端中运行以下命令来检查是否已经安装&#xff1a; node -v…

「scipy、eeg」使用python scipy butter filtfilt 分解EEG数据为5个频带和滤波参数选择

使用scipy butter filtfilt 分解EEG数据和滤波参数选择 【目录】 EEG数据频带和滤波参数滤波类型及示例Pyhton 代码实现 一、EEG数据频带和滤波参数 二、滤波类型 低通滤波&#xff08;lowpass)高通滤波&#xff08;highpass&#xff09;带通滤波&#xff08;bandpass&…

网络传输层TCP协议

传输层TCP协议 1. TCP协议介绍 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一个要对数据的传输进行详细控制的传输层协议。 TCP 与 UDP 的不同&#xff0c;在于TCP是有连接、可靠、面向字节流的。具体来说&#xff0c;TCP设置了一大…