【1++的Linux】之信号量

👍作者主页:进击的1++
🤩 专栏链接:【1++的Linux】

文章目录

  • 一,信号量
  • 二,基于环形队列的生产消费者模型
  • 三,线程池

一,信号量

1,什么是信号量?
任何时候都有一个执行流进入共享资源中,我们将这个共享资源称为临界资源。
若我们将这块共享资源当作整体使用就叫做互斥;那么我要是不想当整体使用,而是想要将这块空间继续细分,让不同的执行流访问不同的区域,那么我们不就可以实现并发了吗!但是我们怎么知道有用了多少资源,还剩多少资源。这就用到信号量,它可以保证我们只要进入这块共享资源,一定可以有一个对应的区域是你的,但是要保证具体哪一个资源是你的需要程序员编码进行控制。

我们以电影院为例:
电影院就是我们的整块共享资源,我们要是让一个人看完后再让另一个人进入去看,效率是不是太低了,因此我们就有了通过先卖票,我只要保证我把票卖给你,就一定有你的位置,买票的本质是不是就是:座位的预定机制。
那么信号量的本质:相当于一把计数器,在访问资源的时候,先申请信号量(sem–),使用完资源后,归还信号量(sem++)。

2,信号量的使用:

信号量的初始化
在这里插入图片描述

pshared:0 表示线程间共享,非零表示进程间共享,我们这里直接设成0即可
value :信号量初始值是多少,你想让信号量是多少,这里填几就可以
返回值:成功就是0,失败就是-1

销毁信号量
int sem_destroy(sem_t *sem);

等待信号量(P操作) 功能:申请信号量,成功会将信号量的值减 1,继续向后执行,失败就挂起等待
int sem_wait(sem_t *sem); //P() 发布信号量(V操作)

功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加 1 。 int sem_post(sem_t *sem);//V()

信号量的P V操作
在这里插入图片描述
多个执行流去访问信号量资源,那么我们就得保证信号量的操作得是原子的,保证信号量是原子的我们才能保证其所保护的资源不会出现错误。

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

1,什么是环形队列
在这里插入图片描述
我们怎么来实现这样一个环形队列呢?
1,双指针链表
2,数组

显然,数组是更加简明一些,那么怎么用数组去实现一个环形呢?
物理结构《------》逻辑结构 我们将线性的物理结构通过模运算,从而可以模拟出环形结构

在这里插入图片描述

index++;
index%=(n+1)

解决了怎么实现环形队列的问题,下面我们再去想想怎么处理其判空判满

  1. 加入计数器
  2. 多申请一个小方格。
    在放数据时先判断:当拿和放指向同一位置时,则队列为空;当放的位置+1==拿的位置,此时说明队列就放慢了。

2,基于环形队列的生产消费者模型原理

生产者,最关心什么资源呢?
环形队列中,空的位置。

消费者,最关心什么资源呢?
环形队列中,数据。

制定规则

规则1:生产者不能把消费者套一个圈(生产者把队列放满以后,不能在继续放一圈数据)。
规则2:消费者不能超过生产者 。
规则3:当指向同一个位置的时候,要根据空,满的状态,来判定让谁先执行(满了只能让消费者走,空了只能让生产走)。
其他:除此之外,消费和生产可以并发执行。

生产者和消费者所看重的资源是不同的,一个在意空位置,一个看重数据。
因此我们就可以定义两个信号量来进行计数,当资源满时,生产者必须要等消费者,资源为空时,消费者必须等生产者。所以量信号量必须要同步。

下面是该模型的简单实现:

#pragma once
#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
#include<functional>
#include<ctime>
#include<vector>
typedef std::function<int(int,int)> func_t;


#define Q_size 5
template<class T>
class Ring_queue
{
public:
    Ring_queue(int res_num=Q_size):dp_(res_num),data_step(0),space_step(0)
    {
        sem_init(&space_sem,0,Q_size);
        sem_init(&data_sem,0,0);
    }

    ~Ring_queue()
    {
        sem_destroy(&space_sem);
        sem_destroy(&data_sem);
    }

    void Push(const T in)
    {
        sem_wait(&space_sem);//P操作申请信号量
       dp_[space_step++]=in;
        std::cout<<"将资源放入仓库中"<<std::endl;
        space_step%=Q_size;
        sem_post(&data_sem);//v操作,资源已经放好,等待来拿

    }

    void Pop(T* out)
    {
        sem_wait(&data_sem);//将这份资源划给相应的执行流,相当于将票卖出去
        std::cout<<"拿到资源"<<std::endl;
        *out=dp_[data_step++];
        data_step%=Q_size;
        sem_post(&space_sem);//拿完后将空间归还

    }

private:
    std::vector<T> dp_;
    int num_;
    sem_t space_sem;
    sem_t data_sem;
    int data_step;
    int space_step;

};
#include"Ring_queue.hpp"

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


class Task
{
public:

    Task()//一定要写,vector在进行初始化时要默认构造,不写此类中就没有默认构造
    {}

    Task(int x,int y,func_t func):x_(x),y_(y),func_(func)
    {
       
    }

  
  int operator ()()
   {
        return func_(x_,y_);
   }

private:
    int x_,y_;
    func_t func_;

};

void* consumer(void* args)
{
    Ring_queue<Task>* rq =(Ring_queue<Task>*)args;
    while(true)
    {
         sleep(2);
        Task ret;
        rq->Pop(&ret);
        std::cout<<"结果"<<ret()<<std::endl;
    }
    
}

void* producer(void* args)
{
    Ring_queue<Task>* rq =(Ring_queue<Task>*)args;
    //制造任务
    std::cout<<"开始"<<std::endl;
   while(true)
   {
     int x=rand()%99;
    int y=rand()%99;
    Task t(x,y,Add);
    rq->Push(t);
   }

}
int main()
{
    srand((unsigned)time(nullptr));
    pthread_t tid[2];
    Ring_queue<Task>* p_rq=new Ring_queue<Task>();
    pthread_create(tid,nullptr,consumer,(void*)p_rq);
    pthread_create(tid+1,nullptr,producer,(void*)p_rq);

    pthread_join(tid[0],nullptr);
    pthread_join(tid[1],nullptr);



    return 0;
}

在这里插入图片描述
多生产多消费者
我们需要再维护生产者和生产者之间,消费者和消费者之间的互斥关系,现在我们不允许两个及两个以上的生产者同时生产或者消费者同时消费,因为所有的生产者共用所有的生产下标,消费者共享所有的消费下标,我们可以引入两把互斥锁维护生产者和消费者各种内部的互斥关系。

在任何时候,生产者可以进入一个,消费者可以进入一个,但是生产和消费却可以同时进行

那么这把锁应该加到哪里呢?
加到申请信号量的后面,我们可以保证,进入生产区域包括更新下标的时候,只有一个执行流进入,如果是多生产者,所有人有资格竞争锁的前提是你必须得先申请信号量,就相当于我们把所有的信号量可以预先分配给所有的生产者,然后当锁一旦释放了,其他人就立马申请锁,在你进入到生产区域的时候,别人就可以并行的先把信号量准备好。
在这里插入图片描述
对生产消费模型的再次理解

生产消费模型可以实现并发:对于基于环形队列的生产消费模型来说,其并发体现在三个点:
1,当队列不为空为满时,生产者和消费者可以进行并发;
2,生产者和生产者,消费者和消费者在申请信号量时可以实现并发。

3,获取数据和处理数据才是最耗时间的,生产消费模型,可以将生产和消费之间进行解耦,通过所谓的仓库,来进行数据的拿和放,这样,当生产者生产的特别快的时候,就可以用多个进程来拿数据,这样也可以实现并发。

三,线程池

#pragma once
#include <iostream>
#include "Thread.hpp"
#include <vector>
#include "Mutex.hpp"
#include <queue>
#include"Task.hpp"


template <class T>
class ThreadPool
{
public:
    static ThreadPool<T> *GetThreadPool()
    {
        if (_tp == nullptr)
        {
            Guard lock(Mtx);
            if (_tp == nullptr)
            {
                _tp = new ThreadPool;
            }
        }

        return _tp;
    }

private:
    ThreadPool(int num = 5) : _num(num)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for (int i = 0; i < _num; i++)
        {
            _pool.push_back(new Thread(i, routine, this));
        }
    }

    ThreadPool<T>(const ThreadPool<T> &th) = delete;
    ThreadPool<T> operator=(ThreadPool th) = delete;

    pthread_mutex_t &getmtx()
    {
        return _mtx;
    }

    pthread_cond_t &getcond()
    {
        return _cond;
    }

    bool TaskPool_Isempty()
    {
        return taskpool.empty();
    }

    T gettask()
    {
        T task = taskpool.front();
        taskpool.pop();
        return task;
    }

public:
    static void *routine(void *args) // 线程取任务
    {
        ThreadData *td = (ThreadData *)args;
        ThreadPool *tp = (ThreadPool *)td->_args;
        while (true)
        {
            T task;
            {
                Guard lockguard(tp->getmtx());
                while (tp->TaskPool_Isempty())
                    pthread_cond_wait(&tp->getcond(), &tp->getmtx());
                task = tp->gettask();
                pthread_mutex_unlock(&Mtx);
            }

            logmessage(NORMAL, "处理任务中");
            // std::cout << task << std::endl;
            task();

            // 处理任务
        }
    }

    void PushTask(T task) // 放任务
    {
        Guard lock(_mtx);
        taskpool.push(task);
        pthread_cond_signal(&_cond);
        logmessage(NORMAL, "放任务中");
    }

    void Run()
    {
        for (auto &it : _pool)
        {
            it->Start();
        }
    }

    ~ThreadPool()
    {
        for (auto &it : _pool)
        {
            it->join();
            delete it;
        }

        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_cond);


    }

private:
    std::vector<Thread *> _pool;
    pthread_cond_t _cond;
    pthread_mutex_t _mtx;
    static ThreadPool<T> *_tp;
    std::queue<T> taskpool;
    static pthread_mutex_t Mtx;
    int _num;
};

template <class T>
pthread_mutex_t ThreadPool<T>::Mtx =  PTHREAD_MUTEX_INITIALIZER;
template <class T>
ThreadPool<T> *ThreadPool<T>::_tp = nullptr;

#pragma once
#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include"log.hpp"
#include<functional>
//using func_t=std::function<void*(void*)> ;
typedef void* (*_func_t)(void*);
class ThreadData
{
public:
    void* _args;
    std::string _name;

};

class Thread
{
public:
    Thread(int num,_func_t func,void* args):_func(func)
    {
        _th_data._args=args;
        char buffer[64];
        snprintf(buffer,sizeof buffer,"%s-%d","thread",num);
        _th_data._name=buffer;
        logmessage(NORMAL,"thread init success");
    }

    void Start()
    {
        pthread_create(&_tid,nullptr,_func,(void*)&_th_data);
        logmessage(NORMAL,"create thread success");
    }

    void join()
    {
        pthread_join(_tid,nullptr);
        logmessage(NORMAL,"join thread success");
    }

    ~Thread()
    {}
private:
    pthread_t _tid;
    _func_t _func;
    ThreadData _th_data;

};

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

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

相关文章

数字技术-IPC专利分类号对应表

数字技术-IPC专利分类号对应表&#xff0c;基于2023年的关键数字技术专利分类体系&#xff0c;通过国际专利分类&#xff08;IPC&#xff09;号进行筛选。这些数据涵盖了各种数字技术领域的创新&#xff0c;包括但不限于人工智能、大数据、云计算、物联网、5G通信等。利用关键词…

Python 进阶(十一):高精度计算(decimal 模块)

《Python入门核心技术》专栏总目录・点这里 文章目录 1. 导入decimal模块2. 设置精度3. 创建Decimal对象4. 基本运算5. 比较运算6. 其他常用函数7. 注意事项8. 总结 大家好&#xff0c;我是水滴~~ 在进行数值计算时&#xff0c;浮点数的精度问题可能会导致结果的不准确性。为了…

lua的gc原理

lua垃圾回收(Garbage Collect)是lua中一个比较重要的部分。由于lua源码版本变迁&#xff0c;目前大多数有关这个方面的文章都还是基于lua5.1版本&#xff0c;有一定的滞后性。因此本文通过参考当前的5.3.4版本的Lua源码&#xff0c;希望对Lua的GC算法有一个较为详尽的探讨。 L…

OpenGL之Mesa3D编译for Ubuntu20.04(三十六)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. 更多原创,欢迎关注:Android…

PGP 遇上比特币

重复使用 PGP 密钥作为比特币密钥 介绍 在数字安全领域&#xff0c;密码学在确保数据的完整性和真实性方面发挥着至关重要的作用。 一种广泛使用的加密技术是使用 Pretty Good Privacy (PGP1)。 PGP 为安全通信&#xff08;例如电子邮件、文件传输和数据存储&#xff09;提供加…

基于单片机寻迹巡线避障智能小车系统设计

**单片机设计介绍&#xff0c; 基于单片机寻迹巡线避障智能小车系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的寻迹巡线避障智能小车系统是一种能够自动跟随线路并避开障碍物的智能小车。下面是一个简要的系…

数据结构与算法编程题28

计算二叉树结点总数 #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std;typedef char ElemType; #define ERROR 0 #define OK 1 #define Maxsize 100 #define STR_SIZE 1024typedef struct BiTNode {ElemType data;BiTNode* lchild, * rchild; }B…

ubuntu 安装 jetbrains-toolbox

ubuntu 安装 jetbrains-toolbox 官网下载 jetbrains-toolbox jetbrains 官网 jetbrains 官网&#xff1a;https://www.jetbrains.com/ jetbrains-toolbox 官网下载页面 在下载页面点击 Download 安装 jetbrains-toolbox 解压 jetbrains-toolbox 安装包 到指定目录 本案例将…

程序的机器级表示

程序的机器级表示 有关CSAPP第三章一些我关注到的重点的记录 操作指令 .c->.exe的流程 1.选项 -E : 预编译过程,处理宏定义和include&#xff0c;并作语法检查 gcc -E hello.c -o hello.i #将hello.c预处理输出为hello.i文件2.选项 -S : 编译过程,生成通用…

【JavaEE】多线程 (1)

目录 1. 认识线程&#xff08;Thread&#xff09; 1) 线程是什么 2) 为啥要有线程 3) 进程和线程的区别 2.第⼀个多线程程序 3.多线程的其他创建方式 方法二:实现 Runnable 接⼝ 方法三:匿名内部类 方法四:实现Runable, 重写run, 匿名内部类 方法五:使用lambda表达式…

爱满荣山·和美岩窝-垃圾分类趣味微课堂

在利州区民政局的支持下&#xff0c;利州社工协会在荣山镇岩窝村开展儿童垃圾分类趣味小课堂。

计算机毕业设计|基于SpringBoot+MyBatis框架的电脑商城的设计与实现(用户上传头像+用户收货管理)

计算机毕业设计|基于SpringBootMyBatis框架的电脑商城的设计与实现&#xff08;用户上传头像&#xff09; 该项目分析着重于设计和实现基于SpringBootMyBatis框架的电脑商城。首先&#xff0c;通过深入分析项目所需数据&#xff0c;包括用户、商品、商品类别、收藏、订单、购物…

Centos7上面部署redis

Centos7上面部署redis 编写这个部署redis&#xff0c;只是为了另一个文章入侵redis做准备&#xff0c;网上还有好多类似的文章&#xff0c;这个单纯的就是部署安装&#xff0c;并简单的测试使用以下 关联其他文章 [1]VMware上面安装部署centos7镜像系统【详细含镜像】 [2]血的教…

计算机组成原理-Cache替换算法

文章目录 总览随机算法&#xff08;RAND&#xff09;先进先出算法&#xff08;FIFO&#xff09;近期最少使用算法&#xff08;LRU&#xff09;最不经常使用算法&#xff08;LFU&#xff09;总结 总览 随机算法&#xff08;RAND&#xff09; 没有选择性地考虑替换哪一块Cache&a…

速通CSAPP(一)计算机系统漫游入门

CSAPP学习 前言 一门经典的计组课程&#xff0c;我却到了大四才学。 anyway&#xff0c;何时都不会晚。 博主参考的教程&#xff1a;本电子书信息 - 深入理解计算机系统&#xff08;CSAPP&#xff09; (gitbook.io)&#xff0c;非常感谢作者的整理。 诚然去看英文版可以学…

谈谈中间件设计的思路

前言 想要设计和真正理解中间件的架构理论和思想。对于开发来说需要具备三个关键的能力 1&#xff1a;基础通用技术的深入理解和运用2&#xff1a;了解和熟悉常见中间件的设计思想&#xff0c;且有自己的感悟,并且能按照自己的理解模仿写一写3&#xff1a;业务的高度理解能力…

解密Spring Cloud微服务调用:如何轻松获取请求目标方的IP和端口

公众号「架构成长指南」&#xff0c;专注于生产实践、云原生、分布式系统、大数据技术分享。 目的 Spring Cloud 线上微服务实例都是2个起步&#xff0c;如果出问题后&#xff0c;在没有ELK等日志分析平台&#xff0c;如何确定调用到了目标服务的那个实例&#xff0c;以此来排…

使用elementPlus去除下拉框蓝色边框

// 下拉框去除蓝色边框 .el-select {--el-select-input-focus-border-color: none !important; }

生成EtherCAT从站XML图片信息方法

0 工具准备 1.PS CS6 2.Hex Editor Neo(文件Hex编辑器) 3.DM3E-556步进电机驱动器 4.TwinCAT(验证XML图片修改效果)1 准备一张需要生成图片信息的图片 根据EtherCAT从站XML图片格式规范,我们需要用到的元素名为ImageData16x14,它要求使用16x14分辨率、深度为16bit的bmp…

【Android Jetpack】Navigation的使用

引入 单个Activity嵌套多个Fragment的UI架构模式&#xff0c;非常非常普遍。但是&#xff0c;对Fragment的管理一直是一件比较麻烦的事情。工程师需要通过FragmentManager和FragmentTransaction来管理Fragment之间的切换。页面的切换通常还包括对应用程序App bar的管理、Fragme…