波奇学Linux:信号量环形队列,原生线程池,封装线程

news2024/11/24 20:39:22

 基于信号量的多生产多消费环形队列

代码

const static int defaultcap=5;

template<class T>
class RingQueue
{
private:
    void P(sem_t &sem)
    {
        sem_wait(&sem); //资源-1
    }
    void V(sem_t &sem)
    {
        sem_post(&sem); //资源加一
    }
    void Lock(pthread_mutex_t &mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    void Unlock(pthread_mutex_ &mutex)
    {
        pthread_mutex_unlock(&mutex);
    }
public:
    RingQueue(int cap=defaultcap)
    :ringqueue_(cap),cap_(cap),c_step_(0),p_step_(0)
    {
        sem_init(&cdata_sem_,0,0); //初始化信号量
        sem_init(&pspace_sem_,0,cap_); //初始化信号量
        pthread_mutex_init(&c_mutex_,nullptr);
        pthread_mutex_init(&c_mutex_,nullptr);
    }
    void Push(const T&in) //生产
    {
        //先申请信号量后申请锁
        //能让两个申请并行,提高并发度
        //申请信号量是原子的
        P(pspace_sem_); 
        Lock(p_mutex_);
        ringqueue_[p_step_]=in;
         // 位置后移,维持环形特征
        p_step_++;
        p_step_%=cap_;
        
        Unlock(p_mutex_);
        V(cdata_sem_);
    }
    void Pop(T* out) //消费
    {
        P(cdata_sem_);
        Lock(c_mutex_);
        *out=ringqueue_[c_step_];
         // 位置后移动
        c_step_++;
        c_step_%=cap_;
        Unlock(c_mutex_);
        V(pspace_sem_);
    
    }
    ~RingQueue()
    {
        sem_destroy(&cdata_sem_); //释放信号量资源
        sem_destroy(&pspace_sem_);
        pthread_mutex_destroy(&c_mutex_);
        pthread_mutex_destroy(&p_mutex_);
    }
private:
    std::vector<T> ringqueue_;
    int cap_;

    int c_step_; //消费者下标
    int p_step_; // 生产者下标
    // 使用信号量可以自动维护互斥性
    sem_t cdata_sem_; //消费者关注的数据资源
    sem_t pspace_sem_; // 生产者关注的空间资源

    pthread_mutex_t c_mutex_; // 多个生产者竞争对信号量的申请
    pthread_mutex_t p_mutex_; // 多个消费者竞争对信号量的申请
};

信号量保证生产者和消费者的同步关系

多线程就要上锁

问题思考 

当多线程访问时如何,锁和信号量谁在前?

信号量申请在前,原因可以使得申请信号量和申请锁行为同步,申请信号量是原子的

当多个线程持有信号量时,会不会对消费资源进行误导?

不会,因为资源的消费取决于cdata_sem_,而当释放c_data_sem此时的确实已经表明资源量加1

当不会出现队列只剩下一个空位,但是有多个线程持有信号量?

不会,因为信号量和空位一一对应的,如果能申请到信号量说明确实有空位

线程池 

接收任务,线程池里面的多个线程分配任务

代码

#pragma once

#include<iostream>
#include<pthread.h>
#include<vector>
#include<queue>
#include<string>
#include<unistd.h>

struct ThreadInfo
{
    pthread_t tid;
    std::string name;
};
static const int defaultnum=5;

template<class T>
class ThreadPool
{
private:
    void Lock()
    {
        pthread_mutex_lock(&mutex_);
    }
    void Unlock()
    {
        pthread_mutex_unlock(&mutex_);
    }
    void Wakeup() //唤醒进程
    {
        pthread_cond_signal(&cond_);
    }
    void ThreadSleep()
    {
        pthread_cond_wait(&cond_,&mutex_);
    }
    bool IsQueueEmpty()
    {
        return tasks_.empty();
    }
public:
    ThreadPool(int num=defaultnum)
    :threads_(num)
    {
        pthread_mutex_init(&mutex_,nullptr);
        pthread_cond_init(&cond_,nullptr);
    }
    //HandlerTask放在类里面会多出一个指针,要加上static
    static void* HandlerTask(void*args)
    {
        ThreadPool<T>*tp=static_cast<ThreadPool<T>*>(args);
        while(true)
        {
            tp->Lock();
            while(tp->IsQueueEmpty())
            {
                tp->ThreadSleep();
            }
            // T t=tp->Pop();
            
            tp->Unlock();
            // t(); //处理任务的同时不涉及共享资源,可以并发指行
        }
    }
    //初始化,创建多个线程
    void Start()
    {
        int num=threads_.size();
        for(int i=0;i<num;i++)
        {
            threads_[i].name="thread-"+std::to_string(i+1);
            pthread_create(&(threads_[i].tid),nullptr,HandlerTask,this);
        }
    }

    void Push(const T&t)
    {
        Lock();
        tasks_.push(t);
        Wakeup();
        Unlock();
    }
    T Pop()
    {
        T t=tasks_.front();
        tasks_.pop();
        return t;
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cond_);
    }
private:
    std::vector<ThreadInfo> threads_;
    std::queue<T> tasks_;
    pthread_mutex_t mutex_;
    pthread_cond_t cond_;

};

问题思考

在类里面调用HandlerTask函数

封装线程

代码

#pragma once
#include<iostream>
#include<string>
#include<ctime>
#include<pthread.h>
typedef void (*callback_t)();
static int num=1;
class Thread
{
public:
    static void *Routine(void*args)
    {
        Thread* thread=static_cast<Thread*>(args);
        thread->Entery();
        return nullptr;
    }
public:
    Thread(callback_t cb)
    :tid_(0)
    ,name_("")
    ,start_timestamp_(0)
    ,isrunning_(false)
    ,cb_(cb)
    {

    }
    void Run()
    {
        name_="thread-" + std::to_string(num++);
        start_timestamp_=time(nullptr);
        isrunning_=true;
        pthread_create(&tid_,nullptr,Routine,this);
    }
    void Join()
    {
        pthread_join(tid_,nullptr);
        isrunning_=false;
    }
    std::string Name();
    uint64_t StartTimestamp();
    bool IsRunning()
    {
        return isrunning_;

    }
    void Entery()
    {
        cb_();
    }
    ~Thread()
    {

    }
private:
    pthread_t tid_; //线程tid
    std::string name_; //线程名字
    uint64_t start_timestamp_; //线程启动时间
    bool isrunning_; // 线程是否运行
    callback_t cb_; //回调函数
};

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

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

相关文章

c++实现简单搜索二叉树<K,V>形

文章目录 搜索二叉树节点类BSTreeNode(节点类的构造) BSTree(功能实现类)Insert(插入)Erase(删除)Find(查找这个节点) 搜索二叉树 搜索二叉树本质:左节点比我小 右节点比我大 节点类 BSTreeNode:给自身节点封装一个类 用这个类来添加节点的操作 我们写的是一个key.value型的搜…

【现代C++】移动语义和右值引用

现代C++中的移动语义和右值引用是C++11引入的重要特性,旨在优化资源管理和提升性能。这些特性允许资源的转移而非复制,特别是对于临时对象和大型对象。 1. 右值引用 右值引用是对临时对象的引用,它允许你修改临时对象。使用&&来声明右值引用。 #include <iost…

【电路笔记】-MOSFET作为开关

MOSFET 作为开关 文章目录 MOSFET 作为开关1、概述2、MOSFET特性曲线2.1 截住区域2.2 饱和区域3、MOSFET作为开关的示例4、功率MOSFET电机控制5、P沟道MOSFET作为开关6、互补MOSFET作为开关电机控制器当 MOSFET 在截止区和饱和区之间工作时,MOSFET 是非常好的电子开关,用于控…

wsl ubuntu 安装的正确方式

目录 wsl ubuntu 安装的正确方式&#xff1a; 将wsl2设置为默认版本&#xff1a; 1、打开powershell 2、设置wsl的版本为2 ​编辑 3、更新wsl程序 4、强制关闭子系统 5、查看wsl支持的列表 6、安装指定版本的系统 wsl ubuntu 安装的正确方式&#xff1a; 此时&#xff0c…

一体成型PFA尖头镊子高纯特氟龙材质镊子适用半导体新材料

PFA镊子用于夹取小型片状、薄状、块状样品&#xff0c;广泛应用在半导体、新材料、新能源、原子能、石油化工、无线电、电力机械等行业。 具有耐高低温性&#xff08;可使用温度-200℃&#xff5e;&#xff0b;260℃&#xff09;、耐腐蚀、表面不粘性等特点&#xff0c;用于苛…

基于Springboot+Redis+mysql实现的闲置二手交易网站管理系统

1.1 背景分析 二手商品是学生比较青睐的廉价商品&#xff0c;网站设计应着重突出实用和廉价。也有一部分消费者是淘宝者&#xff0c;他们对相中的商品有着急切的拥有欲望。网上交易的好学生提供一个供需平台&#xff0c;学生可以将自己不用的东西放在网上&#xff0c;也可在网…

解决quest2激活后更新卡0%(内附全套工具)

注意:顺序很重要,一定是要先开移动热点,再开Netch 下载工具包https://wwi.lanzoup.com/iXaX61rou8hi ,密码在下方 解压后,先安装这个 开启移动热点网络名称和密码随便写下就行 开启后会有个 本地连接 开启Netch添加VLESS 服务器 然后点击启动,启动后 会有一个 aioClou…

设计模式 --3:装扮模式

结构图 代码 #include<iostream>using namespace std;class person { public:person() {};person(string name) { this->name name; }virtual void show() {cout << "装扮的:" << this->name << endl;} private:string name; }; //装…

活动图高阶讲解-05

115 00:06:30,540 --> 00:06:39,060 那我们可以把它和 116 00:06:39,060 --> 00:06:41,260 序列图对比一下 117 00:06:41,260 --> 00:06:41,740 你看 118 00:06:41,740 --> 00:06:42,660 序列图上 119 00:06:42,660 --> 00:06:47,500 同样的一个过程 120 0…

Python之Web开发中级教程----ubuntu中下载安装Postman

Python之Web开发中级教程----ubuntu中下载安装Postman PostMan 是一款功能强大的网页调试与发送网页 HTTP 请求的 Chrome 插件&#xff0c;可以直接去对我们写出来的路由和视图函数进行调试&#xff0c;作为后端程序员是必须要知道的一个工具。 查看ubuntu系统中是否已经安装了…

栈与队列|20.有效的括号

力扣题目链接 class Solution { public:bool isValid(string s) {if (s.size() % 2 ! 0) return false; // 如果s的长度为奇数&#xff0c;一定不符合要求stack<char> st;for (int i 0; i < s.size(); i) {if (s[i] () st.push());else if (s[i] {) st.push(});el…

免费开源:自动会议记录接口调用|语音识别接口|语音识别API

一、开源项目介绍 一款多模态AI能力引擎&#xff0c;专注于提供自然语言处理&#xff08;NLP&#xff09;、情感分析、实体识别、图像识别与分类、OCR识别和语音识别等接口服务。该平台功能强大&#xff0c;支持本地化部署&#xff0c;并鼓励用户体验和开发者共同完善&#xf…

java的前缀和算法

前缀和的概念 对于一个给定的数组A&#xff0c;它的前缀和数组S中S[i]表示从第1个元素到第i个元素的总和&#xff0c;用公式表示为&#xff1a; SiA1A2A3...An 前缀和的作用 在O(1)的时间求出数组任意区间的区间和。 降低求解的复杂度 算法模板 int n10; int [] arrnew in…

Linux 基础-查看和设置环境变量

一&#xff0c;查看环境变量 在 Linux中&#xff0c;环境变量是一个很重要的概念。环境变量可以由系统、用户、Shell 以及其他程序来设定&#xff0c;其是保存在变量 PATH 中。环境变量是一个可以被赋值的字符串&#xff0c;赋值范围包括数字、文本、文件名、设备以及其他类型…

C语言中,基本数据类型介绍

C语言当中各种数据类型的大小&#xff0c;首先要了解有哪些数据类型。 一 字符型&#xff1a; 整数&#xff08;字符&#xff09;类型存储大小值范围char1 字节-128 到 127 或 0 到 255&#xff08;2的8次方&#xff09;unsigned char1 字节0 到 255&#xff08;&#xff09;s…

面试经典-MySQL篇

一、MySQL组成 MySQL数据库的连接池&#xff1a;由一个线程来监听一个连接上请求以及读取请求数据&#xff0c;解析出来一条我们发送过去的SQL语句SQL接口&#xff1a;负责处理接收到的SQL语句查询解析器&#xff1a;让MySQL能看懂SQL语句查询优化器&#xff1a;选择最优的查询…

MATLAB环境下基于决策树和随机森林的心力衰竭患者生存情况预测

近年来&#xff0c;随着医学数据的不断积累和计算机技术的快速发展&#xff0c;许多机器学习技术已经被用在医学领域&#xff0c;并取得了不错的效果。与传统的基于医学知识经验的心衰预后评估模型相比&#xff0c;机器学习方法可以快速、高效地从繁杂的、海量的心衰病人数据中…

SQLite数据库使用指南以及相关API编程

SQLite介绍 SQLite是一种基于C语言开发的轻量级、快速、自包含、高可靠性和全功能的SQL数据库引擎。它是全球范围内使用最为广泛的数据库引擎&#xff0c;被嵌入到所有移动设备和大部分计算机中&#xff0c;并且伴随着无数日常使用的应用程序一起提供。SQLite的文件格式具有稳…

Chapter 13 Techniques of Design-Oriented Analysis: The Feedback Theorem

Chapter 13 Techniques of Design-Oriented Analysis: The Feedback Theorem 从这一章开始讲负反馈Control系统和小信号建模. 13.2 The Feedback Theorem 首先介绍 Middlebrook’s Feedback Theorem 考虑下面负反馈系统 传输函数 Guo/ui G ( s ) u o u i G ∞ T 1 T G…

C/C++火柴棍等式

有n根(n<24)火柴棍&#xff0c;你可以拼出多少个形如“ABC"的等式?等式中的A、B、C是用火柴棍拼出的整数(若该数非零&#xff0c;则最高位不能是0)。用火柴棍拼数字0-9的拼法如图所示: 依次需要用到的火柴棍数目为6 2 5 5 4 5 6 3 7 6 。 如果是初学者可能会这么写。 …