C++笔记之一个轻量级的线程池库threadpool

news2025/1/31 2:56:37

C++笔记之一个轻量级的线程池库threadpool

code review!

抄自:https://github.com/lzpong/

文章目录

  • C++笔记之一个轻量级的线程池库threadpool
    • 1.threadpool.h
    • 2.使用:test2.cc
    • 3.使用:test1.cc
    • 4.代码

1.threadpool.h

在这里插入图片描述

2.使用:test2.cc

在这里插入图片描述

运行(GIF动图)
请添加图片描述

3.使用:test1.cc

在这里插入图片描述

运行(GIF动图)
请添加图片描述

4.代码

threadpool.h

#pragma once
#ifndef THREAD_POOL_H
#define THREAD_POOL_H

#include <atomic>
#include <future>
#include <queue>
#include <vector>
// #include <condition_variable>
// #include <thread>
#include <functional>
#include <stdexcept>

namespace std {
// 线程池最大容量,应尽量设小一点
#define THREADPOOL_MAX_NUM 16
// #define  THREADPOOL_AUTO_GROW

// 线程池,可以提交变参函数或拉姆达表达式的匿名函数执行,可以获取执行返回值
// 不直接支持类成员函数, 支持类静态成员函数或全局函数,Opteron()函数等
class threadpool {
    using Task = function<void()>; // 定义类型
    vector<thread> _pool;          // 线程池
    queue<Task> tasks_queue;       // 任务队列
    mutex _lock;                   // 同步
    condition_variable _task_cv;   // 条件阻塞
    atomic<bool> run_flag{true};   // 线程池是否执行
    atomic<int> _idlThrNum{0};     // 空闲线程数量

  public:
    inline threadpool(unsigned short size = 4) { addThread(size); } // 构造函数,创建线程池并指定初始线程数量,默认为4。
    inline ~threadpool() {                                          // 析构函数,销毁线程池,等待所有任务执行完毕。
        run_flag = false;
        _task_cv.notify_all(); // 唤醒所有线程执行
        for (thread &thread : _pool) {
            // thread.detach();// 让线程“自生自灭”
            if (thread.joinable()) {
                thread.join(); // 等待任务结束, 前提:线程一定会执行完
            }
        }
    }

  public:
    // 提交一个任务
    // 调用.get()获取返回值会等待任务执行完,获取返回值
    // 有两种方法可以实现调用类成员,
    // 一种是使用   bind:.commit(std::bind(&Dog::sayHello, &dog));
    // 一种是用   mem_fn:.commit(std::mem_fn(&Dog::sayHello), this)
    template <class F, class... Args>
    auto commit(F &&f, Args &&...args) -> future<decltype(f(args...))> { // 提交一个任务,执行函数f,可以等待任务执行完毕并获取返回值。
        if (!run_flag) {                                                 // stoped ??
            throw runtime_error("commit on ThreadPool is stopped.");
        }
        using RetType = decltype(f(args...));                                                           // typename std::result_of<F(Args...)>::type, 函数 f 的返回值类型
        auto task = make_shared<packaged_task<RetType()>>(bind(forward<F>(f), forward<Args>(args)...)); // 把函数入口及参数,打包(绑定)
        future<RetType> future = task->get_future();
        // 添加任务到队列
        lock_guard<mutex> lock{_lock};                // 对当前块的语句加锁  lock_guard 是 mutex 的 stack 封装类,构造的时候 lock(),析构的时候 unlock()
        tasks_queue.emplace([task]() { (*task)(); }); // push(Task{...}) 放到队列后面

#ifdef THREADPOOL_AUTO_GROW
        if (_idlThrNum < 1 && _pool.size() < THREADPOOL_MAX_NUM)
            addThread(1);
#endif                         // !THREADPOOL_AUTO_GROW
        _task_cv.notify_one(); // 唤醒一个线程执行

        return future;
    }

    //  获取当前空闲线程数量
    int idlCount() { return _idlThrNum; }
    //  获取线程池中线程的数量。
    int thrCount() { return _pool.size(); }
#ifndef THREADPOOL_AUTO_GROW
  private:
#endif // !THREADPOOL_AUTO_GROW
    // 添加指定数量的线程
    void addThread(unsigned short size) {                               // 添加指定数量的线程到线程池,但不超过预定义的最大线程数量THREADPOOL_MAX_NUM。
        for (; _pool.size() < THREADPOOL_MAX_NUM && size > 0; --size) { // 增加线程数量,但不超过 预定义数量 THREADPOOL_MAX_NUM
            _pool.emplace_back([this] {                                 // 工作线程函数
                while (run_flag) {
                    Task task; // 获取一个待执行的 task
                    // unique_lock 相比 lock_guard 的好处是:可以随时 unlock() 和 lock()
                    unique_lock<mutex> lock{_lock};
                    _task_cv.wait(lock, [this] { return !run_flag || !tasks_queue.empty(); }); // wait 直到有 task
                    if (!run_flag && tasks_queue.empty()) {
                        return;
                    }
                    task = move(tasks_queue.front()); // 按先进先出从队列取一个 task
                    tasks_queue.pop();
                    _idlThrNum--;
                    task(); // 执行任务
                    _idlThrNum++;
                }
            });
            _idlThrNum++;
        }
    }
};

} // namespace std

#endif // https://github.com/lzpong/

test2.cc

#include "threadpool.h"
#include <chrono>
#include <iostream>
using namespace std;

void myTask(int n) {
    cout << "Task " << n << " executed" << endl;
    this_thread::sleep_for(chrono::seconds(2));
}

int main() {
    std::threadpool pool(4); // 创建一个最多包含4个线程的线程池

    // 提交任务给线程池执行
    for (int i = 0; i < 20; ++i) {
        pool.commit(myTask, i);
    }

    this_thread::sleep_for(chrono::seconds(10));

    return 0;
}

// g++ main.cc -o main -lpthread

test1.cc

#include "threadpool.h"
#include <iostream>

void fun1(int slp)
{
    printf("  hello, fun1 !  %d\n" ,std::this_thread::get_id());
    if (slp>0) {
        printf(" ======= fun1 sleep %d  =========  %d\n",slp, std::this_thread::get_id());
        std::this_thread::sleep_for(std::chrono::milliseconds(slp));
    }
}

struct gfun {
    int operator()(int n) {
        printf("%d  hello, gfun !  %d\n" ,n, std::this_thread::get_id() );
        return 42;
    }
};

class A {
public:
    static int Afun(int n = 0) {   //函数必须是 static 的才能直接使用线程池
        std::cout << n << "  hello, Afun !  " << std::this_thread::get_id() << std::endl;
        return n;
    }

    static std::string Bfun(int n, std::string str, char c) {
        std::cout << n << "  hello, Bfun !  "<< str.c_str() <<"  " << (int)c <<"  " << std::this_thread::get_id() << std::endl;
        return str;
    }
};

int main()
    try {
        std::threadpool executor{ 50 };
        A a;
        std::future<void> ff = executor.commit(fun1,0);
        std::future<int> fg = executor.commit(gfun{},0);
        std::future<int> gg = executor.commit(a.Afun, 9999); //IDE提示错误,但可以编译运行
        std::future<std::string> gh = executor.commit(A::Bfun, 9998,"mult args", 123);
        std::future<std::string> fh = executor.commit([]()->std::string { std::cout << "hello, fh !  " << std::this_thread::get_id() << std::endl; return "hello,fh ret !"; });

        std::cout << " =======  sleep ========= " << std::this_thread::get_id() << std::endl;
        std::this_thread::sleep_for(std::chrono::microseconds(900));

        for (int i = 0; i < 50; i++) {
            executor.commit(fun1,i*100 );
        }
        std::cout << " =======  commit all ========= " << std::this_thread::get_id()<< " idlsize="<<executor.idlCount() << std::endl;

        std::cout << " =======  sleep ========= " << std::this_thread::get_id() << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(3));

        ff.get(); //调用.get()获取返回值会等待线程执行完,获取返回值
        std::cout << fg.get() << "  " << fh.get().c_str()<< "  " << std::this_thread::get_id() << std::endl;

        std::cout << " =======  sleep ========= " << std::this_thread::get_id() << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(3));

        std::cout << " =======  fun1,55 ========= " << std::this_thread::get_id() << std::endl;
        executor.commit(fun1,55).get();    //调用.get()获取返回值会等待线程执行完

        std::cout << "end... " << std::this_thread::get_id() << std::endl;


        std::threadpool pool(4);
        std::vector< std::future<int> > results;

        for (int i = 0; i < 8; ++i) {
            results.emplace_back(
                pool.commit([i] {
                    std::cout << "hello " << i << std::endl;
                    std::this_thread::sleep_for(std::chrono::seconds(1));
                    std::cout << "world " << i << std::endl;
                    return i*i;
                })
            );
        }
        std::cout << " =======  commit all2 ========= " << std::this_thread::get_id() << std::endl;

        for (auto && result : results)
            std::cout << result.get() << ' ';
        std::cout << std::endl;
        return 0;
    }
catch (std::exception& e) {
    std::cout << "some unhappy happened...  " << std::this_thread::get_id() << e.what() << std::endl;
}

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

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

相关文章

【医学影像处理】基于MRIcron的dcm2nii批量dcm转nii格式

MRIcron下载 MRIcron下载官网 选择适合自己环境的版本&#xff0c;我使用的是windows版本 MRIcron介绍 MRICron是一款用于处理磁共振成像&#xff08;MRI&#xff09;数据的免费开源软件工具。它是一个功能强大的图像处理程序&#xff0c;主要用于可视化、分析和处理医学影…

代码更换了目录,没有任何变更,但Idea编辑器却提示所有代码都变更了?

开发环境&#xff1a; springboot 2.4.3idea 2020 问题描述&#xff1a; 1、代码copy到U盘了&#xff0c;今天用idea打开U盘代码&#xff0c;却提示所有代码都被修改了 2、diff 文件看了&#xff0c;其实并没有任何修改&#xff0c;因为就算不小心误改了&#xff0c;也不能全…

36.树与二叉树练习(2)(王道第5章综合练习)

试题1&#xff08;王道5.3.3节第16题&#xff09;&#xff1a; 设计算法将二叉树的叶结点按从左到右的顺序连成单链表&#xff0c;连接时使用叶结点的右指针域存放单链表指针。 借助遍历算法完成&#xff1a; //根据二叉树层次遍历序列构造单链表 void LevelOrdertoLinkList…

Qt事件传播机制 day8

Qt事件传播机制 day8 事件的接受和忽略 当空间忽略事件时&#xff0c;事件会继续往上传播&#xff0c;这里的传播指传播给父组件QEvent有accept()函数与ignore()函数 accept()&#xff1a;本组件处理该事件&#xff0c;这个事件就不会被继续传播给其父组件ignore()&#xff1…

spring boot Rabbit高级教程

消息可靠性 生产者重试机制 首先第一种情况&#xff0c;就是生产者发送消息时&#xff0c;出现了网络故障&#xff0c;导致与MQ的连接中断。 为了解决这个问题&#xff0c;SpringAMQP提供的消息发送时的重试机制。即&#xff1a;当RabbitTemplate与MQ连接超时后&#xff0c;…

分类预测 | MATLAB实现SSA-CNN-LSTM-Attention数据分类预测

分类预测 | MATLAB实现SSA-CNN-LSTM-Attention数据分类预测 目录 分类预测 | MATLAB实现SSA-CNN-LSTM-Attention数据分类预测分类效果基本描述模型描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现WOA-CNN-LSTM-Attention数据分类预测&#xff0c;运行环境Matlab2021b及以…

Marin说PCB之BGA焊盘削焊盘带来的焊接问题和解决办法

每周日上午10点钟都是小编最开心的时间了&#xff0c;这个点是斗破苍穹播出的时间。小编我从萧炎从这个动漫开播到现在都追了好多年了&#xff0c;强烈推荐喜欢这个小说的可以看这个动漫&#xff0c;拍的还不错&#xff0c;只是萧炎的配音不再是张沛老师了&#xff0c;有点可惜…

Python实验三

1&#xff1a;编程统计英文句子中的单词出现的次数。 要求&#xff1a;输出结果为按照单词在句子中出现的次数降序排列。 提示&#xff1a;用split&#xff08;&#xff09;拆分字符串 # 1&#xff1a;编程统计英文句子中的单词出现的次数。 # 要求&#xff1a;输出结果为按照…

Java 继承与实现

一、继承&#xff08;extends&#xff09; 1.1 继承概念 继承是面向对象的基本特征&#xff0c;它允许子类继承父类的特征和行为&#xff0c;以提高代码的复用率和维护性等。下面一张图生动地展示了继承和类之间的关系&#xff1a; 继承图 上图中&#xff0c;“动物”、“食草…

【Vue面试题二十一】、Vue中的过滤器了解吗?过滤器的应用场景有哪些?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;Vue中的过滤器了解吗&am…

成为数据分析师要具备什么能力——功法篇(上)

这篇文章适合做了一段时间数据分析工作&#xff0c;开始思考怎么继续提升自己的分析师、运营或者是实习了一段时间的同学&#xff0c;这时的你也许会想几个问题&#xff1a; 为什么我做出来的分析总觉得没有别人的那么高级&#xff1f; 老板为什么总说我的分析“太浅了”&#…

spyder切换conda环境(成功测试)

今天第一次把这个anaconda中配套的spyder的切换环境尝试成功了&#xff0c;特地记录一下 首先明确一点我使用的最新的anaconda的版本是 spyder已经是版本5了&#xff0c;之前的4版本总是出现各种错误 Step1 切换python interpreter 环境是可以直接识别的&#xff0c;不需要…

2023-2024-1 for循环-1(15-38)

7-15 输出闰年 输出21世纪中截止某个年份以来的所有闰年年份。注意&#xff1a;闰年的判别条件是该年年份能被4整除但不能被100整除、或者能被400整除。 输入格式: 输入在一行中给出21世纪的某个截止年份。 输出格式: 逐行输出满足条件的所有闰年年份&#xff0c;即每个年…

华为端到端战略管理体系(DSTE开发战略到执行)的运作日历图/逻辑图及DSTE三大子流程介绍

华为端到端战略管理体系&#xff08;DSTE开发战略到执行&#xff09;的运作日历图/逻辑图及DSTE三大子流程介绍 本文作者 | 谢宁&#xff0c;《华为战略管理法&#xff1a;DSTE实战体系》、《智慧研发管理》作者 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#…

【VASP】KPOINTS文件介绍

【VASP】KPOINTS文件介绍 一、KPOINTS 的两种结构第一种结构&#xff1a;&#xff08;非对称&#xff09;第二种结构&#xff1a;&#xff08;高对称&#xff09; 二、关于KPOINTS设置的一些经验三、KPOINTS的选取 前言 一、4个常用的输入文件INCAR、POSCAR、POTCAR、KPOINTS I…

MySQL [基础]] 学习笔记

MySQL 学习 文章目录 MySQL 学习1. 数据库三层结构2. 数据在数据库中的存储方式3. SQL 语句分类3.1 备份恢复数据库的表 4. Mysql 常用数据类型(列类型)4.1 数值型(整数)的基本使用4.2 数值型(bit)的使用4.3 数值型(小数)的基本使用4.4 字符串的基本使用(面试题)4.5 字符串使用…

基于springboot实现心灵治愈心理健康平台系统项目【项目源码+论文说明】

基于springboot实现心灵心理健康平台系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个心灵治愈交流平台 &#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论…

Linux友人帐之编译器gcc/g++的使用

一、程序的翻译过程 在C语言中&#xff0c;我们已经学过程序的编译和链接&#xff0c;在这里将复习一下我们之前所学的内容并引出后续gcc/g的内容。 1.1程序的翻译过程 预处理&#xff08;头文件展开&#xff0c;去注释&#xff0c;宏替换&#xff0c;条件编译&#xff09;编…

解决:由于找不到 VCRUNTIME140_.dll,无法继续执行代码。重新安装程序可能会解决此问题

最近我在安装一个软件时&#xff0c;遇到过这样的问题“由于找不到 VCRUNTIME140 1.dll&#xff0c;无法继续执行代码。重新安装程序可能会解决此问题”。 要解决这个问题&#xff0c;你可以按照以下步骤进行操作&#xff1a; 1. 重新安装程序&#xff1a; 尝试重新安装使用…

Java NIO模型(提供代码示例)

目录 一、NIO特点介绍二、NIO代码实现2.1、客户端代码2.2、服务端代码 一、NIO特点介绍 NIO全称 java non-blocking IO。从JDK 1.4开始&#xff0c;java提供了一些列改进的输入/输出&#xff08;I/O&#xff09;的新特性&#xff0c;被称为NIO&#xff0c;是同步非阻塞的&…