利用C++11的异步操作实现一个线程池

news2024/11/14 13:54:24

利用C++11的异步操作实现一个线程池

  • 利用C++11的异步操作实现一个线程池
    • 介绍
    • 关于一些代码细节的解释
    • 测试

介绍

基于线程池执行任务的时候,入口函数内部执行逻辑是固定的,因此选择std::packaged_task加上std::future的组合来实现。

具体使用可以见我上一篇博客:C++11中的异步操作|std::future|std::promise|std::packaged_task

线程池的工作思想:

  • 用户传入要执行的函数,以及需要处理的数据(函数的参数),由线程池中的工作线程来执行函数完成任务;

完整代码可以见博客最后,也可以见项目地址: HareMQ/demo/thread_pool

关于一些代码细节的解释

push函数参数的解释

    template <typename func, typename... Args>
    auto push(func&& f, Args&&... args) -> std::future<decltype(f(args...))>;

push 进来的首先是一个函数(用户要执行的函数),接下来是不定参数,表示要处理的数据,也就是要穿入的参数,然后在push内部,把这个函数封装成一个异步操作(packaged_task),丢给线程执行!

返回值的解释

因为我们不知道用户的线程给我们返回的是什么类型的值(因为我们是不知道用户要执行的函数要长什么样的),所以只能用auto表示类型,但是直接用auto肯定是不行的,因为系统不知道要开辟多少空间压栈,所以C++中提供的一个类型推导: decltype(func(args...)) 表示返回类型就是 func(args...) 的返回类型。

push函数内部的一些解释

    auto push(func&& f, Args&&... args) -> std::future<decltype(f(args...))> {
        // 1. 对传入的函数封装成 packaged_task 任务包
        using return_type = decltype(f(args...)); // 返回值类型
        auto bind_func = std::bind(std::forward<func>(f), std::forward<Args>(args)...); // 函数+参数类型
        auto task = std::make_shared<std::packaged_task<return_type()>>(bind_func);
        std::future<return_type> fu = task->get_future();
        // 2. 构造 lambda 匿名函数(捕获任务对象,函数内执行任务对象)
        {
            std::unique_lock<std::mutex> lock(__mtx_lock);
            // 3. 将构造出来的匿名函数对象,抛入到任务队列中
            __task_queue.push_back([task]() {
                (*task)();
            });
            // 4. 唤醒消费者
            __cond.notify_one();
        }
        return fu;
    }

首先,因为每个用户传递进来的函数可能是不一样的,参数返回值都不一样,在push里一定要做一个统一。

首先是返回值类型,我们要做推导:

using return_type = decltype(f(args...)); // 返回值类型

然后我们要把函数和函数参数绑定在一起,所以要用bind,因为是一个可变参数,所以要...展开,因为为了保持参数的性质,需要用一个完美转发std::forward

auto bind_func = std::bind(std::forward<func>(f), std::forward<Args>(args)...); // 函数+参数类型

然后后面就和 asynchronous.md 里面的 demo 一样了,要用一个指针,来封装 std::packaged_task

auto task = std::make_shared<std::packaged_task<return_type()>>(bind_func);
std::future<return_type> fu = task->get_future();

测试

#include "thread_pool.hpp"

int add(int a, int b) { return a + b; }

int main() {
    thread_pool pool;
    for (int i = 0; i < 11; i++) {
        std::future<int> fu = pool.push(add, i, 22);
        LOG(INFO) << "add res: " << fu.get() << std::endl;
    }
    pool.stop();
    return 0;
}

在这里插入图片描述

完整代码

thread_pool.hpp

/*
 * Write by Yufc
 * See https://github.com/ffengc/HareMQ
 * please cite my project link: https://github.com/ffengc/HareMQ when you use this code
 */

#ifndef __YUFC_DEMO_THREAD_POOL__
#define __YUFC_DEMO_THREAD_POOL__

#include "../log.hpp"
#include <assert.h>
#include <condition_variable>
#include <deque>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>

class thread_pool {
public:
    using func_t = std::function<void(void)>;

private:
    std::atomic<bool> __stop_signal; // 线程池停止信号
    std::mutex __mtx_lock; // 互斥锁
    std::condition_variable __cond; // 同步变量
    std::vector<std::thread> __threads; // 线程池的线程
    std::deque<func_t> __task_queue; // 任务队列

public:
    thread_pool(int thread_count = 1)
        : __stop_signal(false) {
        // 创建线程
        for (int i = 0; i < thread_count; i++) {
            __threads.emplace_back(&thread_pool::entry, this);
        }
    }
    ~thread_pool() {
        stop();
    }
    void stop() {
        if (__stop_signal == true)
            return; // 如果已经退出了就不能重复退出了
        __stop_signal = true;
        __cond.notify_all();
        for (auto& e : __threads) // 等待所有线程退出
            e.join();
    }

    template <typename func, typename... Args>
    auto push(func&& f, Args&&... args) -> std::future<decltype(f(args...))> {
        // 1. 对传入的函数封装成 packaged_task 任务包
        using return_type = decltype(f(args...)); // 返回值类型
        auto bind_func = std::bind(std::forward<func>(f), std::forward<Args>(args)...); // 函数+参数类型
        auto task = std::make_shared<std::packaged_task<return_type()>>(bind_func);
        std::future<return_type> fu = task->get_future();
        // 2. 构造 lambda 匿名函数(捕获任务对象,函数内执行任务对象)
        {
            std::unique_lock<std::mutex> lock(__mtx_lock);
            // 3. 将构造出来的匿名函数对象,抛入到任务队列中
            __task_queue.push_back([task]() {
                (*task)();
            });
            // 4. 唤醒消费者
            __cond.notify_one();
        }
        return fu;
    }

private:
    // 线程入口函数 -- 不断从任务队列中取出任务进行执行
    void entry() {
        while (!__stop_signal) {
            std::deque<func_t> tmp;
            {
                // 1. 加锁
                std::unique_lock<std::mutex> lock(__mtx_lock);
                // 2. 任务队列不为空,或者 __stop_signal 被置位, 否则就一直等着
                __cond.wait(lock, [this]() { return __stop_signal || !__task_queue.empty(); });
                // 3. 因为现在是加锁状态,一次取一个太亏了,如果就绪多个,可以一次取出来的}
                tmp.swap(__task_queue);
                assert(__task_queue.size() == 0); // 此时任务队列应该为空了
            }
            for (auto& t : tmp) // 逐个执行即可(无锁状态)
                t();
        }
    }
};
#endif

test.cc

/*
 * Write by Yufc
 * See https://github.com/ffengc/HareMQ
 * please cite my project link: https://github.com/ffengc/HareMQ when you use this code
 */

#include "thread_pool.hpp"

int add(int a, int b) { return a + b; }

int main() {
    thread_pool pool;
    for (int i = 0; i < 11; i++) {
        std::future<int> fu = pool.push(add, i, 22);
        LOG(INFO) << "add res: " << fu.get() << std::endl;
    }
    pool.stop();
    return 0;
}

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

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

相关文章

2025年第7届图像处理和机器视觉国际会议 (IPMV 2025)即将召开!

2025年第7届图像处理和机器视觉国际会议 (IPMV 2025)将于2025年1月10日-12日在中国香港举行。图像处理和机器视觉作为当代信息技术领域的重要分支&#xff0c;不仅推动了人工智能技术的飞速发展&#xff0c;也为各行各业带来了革命性的变革。本次会议旨在汇聚全球图像处理和机器…

极致的灵活度满足工程美学:用Vue Flow绘制一个完美流程图

目录 极致的灵活度满足工程美学&#xff1a;用Vue Flow绘制一个完美流程图 一、环境要求 二、初识Vue Flow 2.1、安装Vue Flow 2.2、Vue Flow构成 2.3、一个小坑 2.4、入门案例 三、Vue Flow优秀的自定义功能 3.1、引入 3.2、节点与连线的自定义 ①打样&#xff08;…

MySQL - 通过SQL语句导出数据到CSV文件

在 MySQL 中&#xff0c;可以使用 SELECT ... INTO OUTFILE 语句将查询结果导出为 CSV 文件&#xff0c;然后再将 CSV 文件转换为 Excel 格式。以下是一个示例&#xff1a; SELECT column1, column2, column3 INTO OUTFILE /path/to/file.csv FIELDS TERMINATED BY , ENCLO…

git 迁移仓库的方法

git Git是一个开源的分布式版本控制系统&#xff0c;由Linus Torvalds在2005年创建&#xff0c;用于有效、高速地处理从小到大的项目管理。它最初是为Linux内核开发而设计的&#xff0c;但很快被广泛用于各种项目。 以下是Git的一些主要特性&#xff1a; 分布式架构&#xff…

接近传感器 - 从零开始认识各种传感器【第十七期】

1、什么是接近传感器 接近传感器常被用于检测物体的距离或者是否有物体靠近。它通常发射电磁场或电磁波&#xff08;例如红外线&#xff09;来探测物体的位置。当有物体靠近时&#xff0c;传感器会接收到反射的电磁波信号&#xff0c;从而触发相应的电路或者控制系统。它广泛应…

Helm(二)

一、Chart模板流程控制if_with_range 1.if 修改values.yaml cat > values.yaml <<EOF myname: yeunyi service: type: ClusterIP port: 80 myport: 8080 EOF 修改service.yaml cat > templates/service.yaml <<EOF apiVersion: v1 kind: Service met…

TC8:SOMEIP_ETS_007-008

SOMEIP_ETS_007: echoBitfields 目的 检查位字段是否能够被顺利地发送和接收。 测试步骤 Tester:创建SOME/IP消息Tester:使用method echoBitfields发送SOME/IP消息DUT:返回method响应消息,其中位字段的顺序与请求相比是反向的期望结果 3、DUT:返回method响应消息,其中位…

微软蓝屏”事件暴露了网络安全哪些问题?

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

探秘数字孪生技术在智慧校园的应用

在当今教育信息化的浪潮中&#xff0c;数字孪生技术逐渐成为智慧校园建设的重要助推器。这一技术通过构建真实世界的数字化映像&#xff0c;不仅提升了校园管理的效率&#xff0c;更为师生的日常学习生活提供了全新的体验。首先&#xff0c;数字孪生可以将校园内的各类设施、环…

七夕告白攻略:天使智能体教你如何设计完美表白卡片!独属程序员地浪漫!

文章目录 &#x1f495;七夕浪漫告白天使&#x1f495;&#x1f495;浪漫风格的表白卡片设计&#x1f495;&#x1f495;甜蜜风格的表白卡片设计&#x1f495;&#x1f495;温馨风格的表白卡片设计&#x1f495;&#x1f495;幽默风格的表白卡片设计&#x1f495;&#x1f495;…

使用java读取本地文件内容并输出,java读取文件内容,节省内存开销

java使用FileInputStream读取本地文件内容 java使用Stream流读取本地文件内容 1.先在自己笔记本选一个目录创建文件&#xff0c;这里就选择在D盘创建一个 word.txt文件 随意输入内容例如 2.直接来直接复制代码运行 import java.io.*; import java.nio.file.Files; import ja…

rust sip电话(基于tauri 、pjsip库)

本文尝试下rust 的tauri 桌面运用 原因在于体积小 1、pjsip 提供了rust 接口官方的 rust demo 没编译出来 在git找了个sip-phone-rs-master https://github.com/Charles-Schleich/sip-phone-rs 可以自己编译下pjsip lib库替换该项目的lib 2、创建一个tauri demo 引用 [depe…

佐糖v1.7.2高级版,全能型AI图片处理App!

经常刷抖音的小伙伴应该都遇见过这种吧&#xff0c;修复老张片一张10块钱&#xff0c;老照片还原高清...这种看似很高端很神器&#xff0c;你以为真的有画师专门帮你来修复老张片吗&#xff1f;&#xff01;怎么可能...都是现在AI技术识别照片信息&#xff0c;然后给你生成的。…

全国禁毒知识竞赛--“我是答题王”竞赛规则

全国禁毒知识竞赛共分四轮&#xff0c;通过6支队伍最终分数高低&#xff0c;角逐出团队一、二、三等奖。第四轮各队派出1名代表进行车轮挑战&#xff0c;最终决出本届的“我是答题王”一、二、三等奖和个人答题王。 环节一&#xff1a;火线缉毒&a…

基于CNN的手写汉字识别系统的设计与实现

1 项目介绍 1.1 摘要 随着数字化技术的快速发展&#xff0c;手写汉字识别技术在多个领域展现出广泛的应用前景。为了提升手写汉字识别的准确率和泛化能力&#xff0c;本文通过构建一个基于卷积神经网络&#xff08;CNN&#xff09;的手写汉字识别系统&#xff0c;以应对不同书…

Nginx系列-11 HTTP消息处理流程

背景 了解Nginx处理HTTP请求的11个阶段&#xff0c;有助于理解和配置nginx、自定义模块、基于lua模块自定义功能。按如下配置&#xff0c;执行"curl http://localhost:8001/query/test.html"&#xff0c;如果读者对结果不是很确定&#xff0c;建议阅读本文。 serve…

免费pdf转word软件有哪些?5个软件帮助你快速进行文件转换

免费pdf转word软件有哪些&#xff1f;5个软件帮助你快速进行文件转换 将PDF文件转换为Word格式可以方便编辑和修改。以下是五款免费的PDF转Word软件&#xff0c;它们可以帮助你快速进行文件转换。 迅捷PDF转换器 这是一款设计简洁、操作便捷的PDF工具&#xff0c;它集成了多…

go语言day20 使用gin框架获取参数 使用自定义的logger记录日志

Golang 操作 Logger、Zap Logger 日志_golang zap-CSDN博客 目录 一、 从控制器中获取参数的几种形式 1&#xff09; 页面请求url直接拼接参数。 2&#xff09; 页面请求提交form表单 3&#xff09; 页面请求发送json数据&#xff0c;使用上下文对象c的BindJSON()方法接…

面试场景题系列--(4)设计一个支持敏感数据存储和传输安全的加解密平台--xunznux

文章目录 设计一个支持敏感数据存储和传输安全的加解密平台1. 设计背景2. 需求分析日常开发中的加解密程序常见问题解决方案具体来说系统主要用例过程和功能系统需求 3. 概要设计3.1 部署模型3.2 加解密调用流程 4. 详细设计4.1 密钥领域模型4.2 核心服务类设计4.3 加解密数据接…

张量网络碎碎念:从 SO3 到 SO2

在上两篇文献中&#xff0c;我像大家介绍了 多通道 模型在 AI for Science 任务中的应用。核心思路类似 CV 中&#xff0c;将灰白单通道拓展到 RGB 多通道&#xff0c;能够提升图片表征能力。&#xff08;见 图神经网络与分子表征&#xff1a;8. TFN&#xff09;痛点在于张量积…