【Linux】多线程_6

news2024/9/21 20:34:40

文章目录

  • 九、多线程
    • 7. 生产者消费者模型
      • 生产者消费者模型的简单代码
      • 结果演示
  • 未完待续


九、多线程

7. 生产者消费者模型

生产者消费者模型的简单代码

Makefile

cp:Main.cc
	g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
	rm -f cp

Thread.hpp

#ifndef __THREAD_HPP__
#define __THREAD_HPP__

#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <pthread.h>

namespace ThreadModule
{
    template<typename T>
    using func_t = std::function<void(T&)>;

    template<typename T>
    class Thread
    {
    public:
        void Excute()
        {
            _func(_data);
        }
    public:
        Thread(func_t<T> func, T& data, const std::string &name="none-name")
            : _func(func)
            , _data(data)
            , _threadname(name)
            , _stop(true)
        {}

        static void *threadroutine(void *args)
        {
            Thread<T> *self = static_cast<Thread<T> *>(args);
            self->Excute();
            return nullptr;
        }

        bool Start()
        {
            int n = pthread_create(&_tid, nullptr, threadroutine, this);
            if(!n)
            {
                _stop = false;
                return true;
            }
            else
            {
                return false;
            }
        }

        void Detach()
        {
            if(!_stop)
            {
                pthread_detach(_tid);
            }
        }

        void Join()
        {
            if(!_stop)
            {
                pthread_join(_tid, nullptr);
            }
        }

        std::string name()
        {
            return _threadname;
        }

        void Stop()
        {
            _stop = true;
        }

        ~Thread() {}
    private:
        pthread_t _tid;
        std::string _threadname;
        T& _data;
        func_t<T> _func;
        bool _stop;
    };
}

#endif

BlockQueue.hpp

#ifndef __BLOCKQUEUE_HPP__
#define __BLOCKQUEUE_HPP__

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

template<typename T>
class BlockQueue
{
private:
    bool IsFull() const
    {
        return _block_queue.size() == _cap;
    }

    bool IsEmpty() const
    {
        return _block_queue.empty();
    }
public:
    BlockQueue(int cap)
        :_cap(cap)
    {
        _productor_wait_num = 0;
        _consumer_wait_num = 0;
        // 初始化互斥锁
        pthread_mutex_init(&_mutex, nullptr);
        // 初始化条件变量
        pthread_cond_init(&_productor_cond, nullptr); 
        // 初始化条件变量
        pthread_cond_init(&_consumer_cond, nullptr);
    }

    // 生产者使用的入队列接口
    void Enqueue(const T& in)
    {
        // 加锁
        pthread_mutex_lock(&_mutex);
        while (IsFull())
        {
            // 生产者等待数量加1
            _productor_wait_num++;
            // 等待条件变量通知唤醒并竞争到互斥锁
            pthread_cond_wait(&_productor_cond, &_mutex);
            // 生产者等待数量减1
            _productor_wait_num--;
        }
        // 生产的数据入资源队列
        _block_queue.push(in);
        // 解锁
        pthread_mutex_unlock(&_mutex);
        // 通知消费者可以从等待队列中出队列
        if (_consumer_wait_num > 0) pthread_cond_signal(&_consumer_cond);
    }

    // 消费者使用的出队列接口
    void Pop(T* out)
    {
        // 加锁
        pthread_mutex_lock(&_mutex);
        while (IsEmpty())
        {
            // 消费者等待数量加1
            _consumer_wait_num++;
            // 等待条件变量通知唤醒并竞争到互斥锁
            pthread_cond_wait(&_consumer_cond, &_mutex);
            // 消费者等待数量减1
            _consumer_wait_num--;
        }
        // 获取数据
        *out = _block_queue.front();
        // 数据出队列
        _block_queue.pop();
        // 解锁
        pthread_mutex_unlock(&_mutex);
        // 通知生产者可以从等待队列中出队列
        if (_productor_wait_num > 0) pthread_cond_signal(&_productor_cond);
    }

    ~BlockQueue()
    {
        // 销毁互斥锁
        pthread_mutex_destroy(&_mutex);
        // 销毁条件变量
        pthread_cond_destroy(&_productor_cond);
        // 销毁条件变量
        pthread_cond_destroy(&_consumer_cond);
    }
private:
    std::queue<T> _block_queue;
    // 容量上限
    int _cap;
    // 互斥锁
    pthread_mutex_t _mutex;
    // 条件变量,用于通知生产者可以入队列
    pthread_cond_t _productor_cond;
    // 条件变量,用于通知消费者可以出队列
    pthread_cond_t _consumer_cond;
    // 生产者等待数量
    int _productor_wait_num;
    // 消费者等待数量
    int _consumer_wait_num;
};

#endif

Task.hpp

#pragma once

#include <iostream>
#include <string>


class Task
{
public:
    Task()
    {}

    Task(int a, int b)
        :_a(a)
        ,_b(b)
        ,_result(0)
    {}

    // 执行任务
    void Execute()
    {
        _result = _a + _b;
    }

    std::string ResultToString()
    {
        return std::to_string(_a) + " + " + std::to_string(_b) + " = " + std::to_string(_result);
    }

    std::string DebugToString()
    {
        return std::to_string(_a) + " + " + std::to_string(_b) + " = ?";
    }
private:
    int _a;
    int _b;
    int _result;
};

Main.cc

#include "BlockQueue.hpp"
#include "Thread.hpp"
#include "Task.hpp"
#include <string>
#include <vector>
#include <unistd.h>

using namespace ThreadModule;
// 创建类型别名
using blockqueue_t = BlockQueue<Task>;

// 消费者线程
void Consumer(blockqueue_t& bq)
{
    while (true)
    {
        Task t;
        // 从阻塞队列中获取任务资源
        bq.Pop(&t);
        // 执行任务
        t.Execute();
        // 输出结果
        std::cout << "Consumer: " << t.ResultToString() << std::endl;
    }
}

// 生产者线程
void Productor(blockqueue_t& bq)
{
    srand(time(nullptr)^pthread_self());
    while (true)
    {
        // 分配任务
        int a = rand() % 10 + 1;
        usleep(1234);
        int b = rand() % 20 + 1;
        Task t(a, b);
        // 任务放入阻塞队列
        bq.Enqueue(t);
        // 输出任务信息
        std::cout << "Productor: " << t.DebugToString() << std::endl;
        sleep(1);
    }
}

// 启动线程
void StartComm(std::vector<Thread<blockqueue_t>>* threads, int num, blockqueue_t& bq, func_t<blockqueue_t> func)
{
    for (int i = 0; i < num; i++)
    {
        // 创建一批线程
        std::string name = "thread-" + std::to_string(i + 1);
        threads->emplace_back(func, bq, name);
        threads->back().Start();
    }
}

// 创建消费者线程
void StartConsumer(std::vector<Thread<blockqueue_t>>* threads, int num, blockqueue_t& bq)
{
    StartComm(threads, num, bq, Consumer);
}

// 创建生产者线程
void StartProductor(std::vector<Thread<blockqueue_t>>* threads, int num, blockqueue_t& bq)
{
    StartComm(threads, num, bq, Productor);
}

// 等待所有线程结束
void WaitAllThread(std::vector<Thread<blockqueue_t>>& threads)
{
    for (auto& thread : threads)
    {
        thread.Join();
    }
}

int main()
{
    // 创建阻塞队列,容量为5
    blockqueue_t* bq = new blockqueue_t(5);
    // 创建线程
    std::vector<Thread<blockqueue_t>> threads;
    // 创建 1个消费者线程
    StartConsumer(&threads, 1, *bq);
    // 创建 1个生产者线程
    StartProductor(&threads, 1, *bq);

    // 等待所有线程结束
    WaitAllThread(threads);

    return 0;
}

结果演示

在这里插入图片描述
这里使用的是单生产者和单消费者,当然也可以在主函数处创建多生产者和多消费者的模型。


未完待续

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

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

相关文章

React学习笔记02-----

一、React简介 想实现页面的局部刷新&#xff0c;而不是整个网页的刷新。AJAXDOM可以实现局部刷新 1.特点 &#xff08;1&#xff09;虚拟DOM 开发者通过React来操作原生DOM&#xff0c;从而构建页面。 React通过虚拟DOM来实现&#xff0c;可以解决DOM的兼容性问题&#x…

NSSCTF_RE(一)暑期

[SWPUCTF 2021 新生赛]简单的逻辑 nss上附件都不对 没看明白怎么玩的 dnspy分析有三个 AchievePoint , game.Player.Bet - 22m; for (int i 0; i < Program.memory.Length; i) { byte[] array Program.memory; int num i; array[num] ^ 34; } Environment.SetEnvironment…

【CICID】GitHub-Actions-SpringBoot项目部署

[TOC] 【CICID】GitHub-Actions-SpringBoot项目部署 0 流程图 1 创建SprinBoot项目 ​ IDEA创建本地项目&#xff0c;然后推送到 Github 1.1 项目结构 1.2 Dockerfile文件 根据自身项目&#xff0c;修改 CMD ["java","-jar","/app/target/Spri…

Scrapy框架实现数据采集的详细步骤

需求描述&#xff1a; 本项目目标是使用Scrapy框架从宁波大学经济学院网站&#xff08;nbufe.edu.cn&#xff09;爬取新闻或公告详情页的内容。具体需求如下&#xff1a; 1、通过遍历多个页面&#xff08;共55页&#xff09;构建翻页URL。 2、使用scrapy自带的xpath从每页的…

STM32智能机器人避障系统教程

目录 引言环境准备智能机器人避障系统基础代码实现&#xff1a;实现智能机器人避障系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;机器人导航与避障问题解决方案与优化收尾与总结 1. 引言 智能机器人避…

Android ImageDecoder把瘦高/扁平大图相当于fitCenter模式decode成目标小尺寸Bitmap,Kotlin

Android ImageDecoder把瘦高/扁平大图相当于fitCenter模式decode成目标小尺寸Bitmap&#xff0c;Kotlin val sz Size(MainActivity.SIZE, MainActivity.SIZE)val src ImageDecoder.createSource(mContext?.contentResolver!!, uri)val bitmap ImageDecoder.decodeBitmap(sr…

iPhone数据恢复篇:在 iPhone 上恢复找回短信的 5 种方法

方法 1&#xff1a;检查最近删除的文件夹 iOS 允许您在 30 天内恢复已删除的短信。您需要先从“设置”菜单启用“过滤器”。让我们来实际检查一下。 步骤 1&#xff1a;打开“设置” > “信息”。 步骤 2&#xff1a;选择“未知和垃圾邮件”&#xff0c;然后切换到“过滤…

全开源批量SEO外链工具html源码

全开源批量SEO外链工具html源码&#xff0c;已更新增加大量高质量外链 若需要增加修改其他外链请打开txt文件 修复优化页面端代码&#xff0c;界面布局 源码为自适应端&#xff0c;手机和电脑端都适配 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725…

Redis作为缓存,如何保证MySQL数据库与Redis缓存一致性(双写一致性)?

双写一致性&#xff1a;当修改了数据库的数据也要同时更新缓存的数据&#xff0c;缓存和数据库的数据要保持一致。 强一致性&#xff1a;如果项目要求数据库与Redis保持高度一致&#xff0c;可以采用读写锁保证强一致性。采用redisson实现的读写锁&#xff0c;在读的时候添加共…

基于单片机的停车场车位管理系统设计

1.简介 停车场车位管理系统是日常中随处可见的一种智能化车位管理技术&#xff0c;使用该技术可以提高车位管理效率&#xff0c;从而减轻人员车位管理工作负荷。本系统集成车牌识别、自动放行、自助缴费等技术&#xff0c;并且具备车位占用状态实时监测与车位数量实时统计、查询…

论文翻译:Rethinking Interpretability in the Era of Large Language Models

https://arxiv.org/abs/2402.01761 在大型语言模型时代的可解释性再思考 摘要 在过去十年中&#xff0c;随着越来越大的数据集和深度神经网络的兴起&#xff0c;可解释机器学习领域的兴趣迅速增长。同时&#xff0c;大型语言模型&#xff08;LLMs&#xff09;在广泛的任务中…

STM32-寄存器点灯案例详解

本文以PA1引脚点亮LED灯为案例&#xff0c;解析了STM32寄存器操作的配置过程&#xff0c;以及从手册查询方法和寄存器配置步骤。 一、概念 1.十六进制和二进制之间相互转换关系 首先&#xff0c;需要了解十六进制和二进制之间的基本转换方法。十六进制是一种基数为16的数制&…

如何在VS200和VScode里面查看数组全部值

如何在VS200和VScode里面查看数组全部值 如何在VS200和VScode里面查看数组全部值 如何在VS200和VScode里面查看数组全部值 需要在调试阶段&#xff0c;在监视窗口添加表达式即可 第一种是解包&#xff0c;能够从0开始查看指定元素个数 第二种是指针索引&#xff0c;能够从0开…

Dpm-tse:目标声音提取的扩散概率模型

第二章 目标说话人提取之《DPM-TSE: A DIFFUSION PROBABILISTIC MODEL FOR TARGET SOUND EXTRACTION》 文章目录 前言一、任务二、动机三、挑战四、方法1.概率扩散模型2.修正噪音时间表和采样步骤3. 模型框架4. 五、实验评价1.数据集2.消融实验3.客观评价4.主观评价 六、结论七…

链接追踪系列-10.mall-swarm微服务运行并整合elk-上一篇的番外

因为上一篇没对微服务代码很详细地说明&#xff0c;所以在此借花献佛&#xff0c;使用开源的微服务代码去说明如何去做链路追踪。 项目是开源项目&#xff0c;fork到github以及gitee中&#xff0c;然后拉取到本地 后端代码&#xff1a; https://gitee.com/jelex/mall-swarm.gi…

全栈 Discord 克隆:Next.js 13、React、Socket.io、Prisma、Tailwind、MySQL笔记(一)

前言 阅读本文你需要有 Next.js 基础 React 基础 Prisma 基础 tailwind 基础 MySql基础 准备工作 打开网站 https://ui.shadcn.com/docs 这不是一个组件库。它是可重用组件的集合&#xff0c;您可以将其复制并粘贴到应用中。 打开installation 选择Next.js 也就是此页面…

C字符串和内存函数介绍(三)——其他的字符串函数

在#include<string.h>的这个头文件里面&#xff0c;除了前面给大家介绍的两大类——长度固定的字符串函数和长度不固定的字符串函数。还有一些函数以其独特的用途占据一席之地。 今天要给大家介绍的是下面这三个字符串函数&#xff1a;strstr&#xff0c;strtok&#xf…

php 可逆与不可逆加密函数

https://andi.cn/page/621536.html

基于机器学习的锂离子电池容量估计(MATLAB R2021B)

锂离子电池已经广泛应用于电动汽车或混合动力汽车的能源存储装置。由于电化学成分的衰退&#xff0c;锂离子电池随着使用时间的增加&#xff0c;电池性能不断退化&#xff0c;导致电池容量和功率发生衰退。电池容量衰退的因素主要有金属锂沉积&#xff0c;活性物质分解和电解液…

周报(1)<仅供自己学习>

文章目录 一.pytorch学习1.配置GPU2.数据读取问题1&#xff08;已解决问题2&#xff08;已解决 3.卷积的学习 二.NeRF学习1.介绍部分问题1&#xff08;已解决 2.神经辐射场表示问题2&#xff08;已解决问题3&#xff08;已解决问题4&#xff08;已解决问题5&#xff1a;什么是视…