C++ 多线程:实现一个功能完整的线程池

这篇具有很好参考价值的文章主要介绍了C++ 多线程:实现一个功能完整的线程池。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

C++ 多线程(四):实现一个功能完整的线程池

        今天我们来聊一聊异步编程的知识。在分布式系统中,一个功能完整的线程池类是一切代码的前提。

一个『合格』的线程池该具备哪些功能?

首先,很自然地想到『线程池类里该有个线程对象的集合,然后可以初始化线程对象的个数、创建线程对象、及启动线程主函数』。没错,这些是基本功能,但是,它更重要的功能是『这些线程对象该运行哪些任务,以及怎么运行这些任务』

解决方案就是任务队列,这个队列里装载了各类各样的任务(函数对象、函数指针、仿函数等)。接着,每个线程去循环遍历这个任务队队列,然后执行该任务。所以,这个类的成员组成大致就清晰了:

  • 一个线程对象队列及初始化方法
  • 一个任务队列及任务添加的方法

功能完整的线程池类实现

#include <iostream>
#include <cstring>
#include <stdexcept>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <mutex>
#include <deque>
#include <thread>
#include <pthread.h>
#include <unistd.h>
#include <memory>
#include <condition_variable>
#include <future>
#include <list>

// 线程安全队列
template <typename T>
class ThreadSafeQue {
public:
    ThreadSafeQue() {};
    ~ThreadSafeQue() {};
    bool empty()
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        return queue_.empty();
    }

    bool pop(T&& pData)
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        if (queue_.empty()) {
            return false;
        }
        pData = std::move(queue_.front());
        queue_.pop_front();
        return true;
    }
    void push(T&& item)
    {
        std::unique_lock<std::mutex> mlock(mutex_);
        queue_.push_back(std::move(item));
        mlock.unlock();    
        cond_.notify_all(); 
    }

private:
    std::deque<T> queue_;
    std::mutex mutex_;
    std::condition_variable cond_;
};

// 线程池类
template<class T>
class ThreadPool {
public:
    ThreadPool(uint32_t i, uint32_t j) : _thread_num(i), _task_num(j) {}

    ~ThreadPool() {}

    void Run() {
        for (std::thread &t : _threads) {
            t.join();  // 等待子线程执行结束
        }
    }

    void Init() {
        for (int i = _thread_num; i > 0; --i) {
            _threads.emplace_back([this, i]() {  // 线程对象创建的同时就开始执行
                std::packaged_task<T()> task;
                while(task_queue.pop(std::move(task))) {
                    std::unique_lock<std::mutex> ul(mtx);
                    std::cout << "curr thread id: " << i << "\n";
                    task();
                }
            });
        }
    }

    template <class Callable, class... Args>
    void AddTask(Callable &&func, Args &&... args) {
        std::packaged_task<T()> task(std::bind(std::forward<Callable>(func), std::forward<Args>(args)...));
        ret_status.push_back(task.get_future());
        task_queue.push(std::move(task));
    }

public:
    uint32_t _thread_num;
    uint32_t _task_num;
    ThreadSafeQue<std::packaged_task<T()>> task_queue;  // 线程任务队列
    std::vector<std::thread> _threads;  // 线程对象池
    std::mutex mtx;
    std::vector<std::future<T>> ret_status;
};

class Func {
    public:
    void operator()(int k) {
        std::cout << k << "\n";
        usleep(1000);
    }
};

template <class T>
void FillTaskQue(ThreadPool<T>* tp) {
    for (int i = 0; i < tp->_task_num; i++) {
        Func func;
        tp->AddTask(func, i);
    }
}

int main()
{
    using T = void;
    ThreadPool<T> threadPool(4, 10);
    std::future<T> ret = std::async(FillTaskQue<T>, &threadPool);  
    //ret.wait();  
    threadPool.Init();
    threadPool.Run();
    return 0;
}

上述代码中,首先实现了一个简单的线程安全队列 ThreadSafeQue,用来存放任务。

线程池类 ThreadPool 中定义了 AddTask 模板方法用来添加线程任务(也就是函数对象 Func func)。ret_status 记录了任务执行的完成情况。

上述实现代码量虽然不多,但涉及的知识点还是挺多的:

  • std::async 启动一个任务队列填充的线程
  • std::forward 通常是用于完美转发的,它会将输入的参数原封不动地传递到下一个函数中,
    这个“原封不动”指的是,如果输入的参数是左值,那么传递给下一个函数的参数的也是左值;如果输入的参数是右值,那么传递给下一个函数的参数的也是右值
  • std::packaged_task 包装一个可调用的对象,并且允许异步获取该可调用对象产生的结果,从包装可调用对象意义上来讲,std::packaged_task 与 std::function 类似,只不过 std::packaged_task 将其包装的可调用对象的执行结果传递给一个 std::future 对象(该对象通常在另外一个线程中获取 std::packaged_task 任务的执行结果)。
  • 函数的返回结果用 std::future 接收,可以调用 future.get 等待异步任务执行完成,调用 future.wait 等待异步任务开始执行,future.wait_for 超时等待函数返回结果,但是它的模板类型只能是函数的返回值类型,局限性比较大,可以结合 std::promise 使用,更加灵活。std::promise 也是一个模板类,它的作用是在不同的线程中实现数据的同步,set_value()则直接将 future 的状态设置为ready,使用方法如下:
auto promise = std::,make_shared<std::promise<int32_t>>();
promise->set_value(value)
std::future<int> fut = promise->get_future();

最后,我们再来看下线程池类的测试结果吧。

C++ 多线程:实现一个功能完整的线程池文章来源地址https://www.toymoban.com/news/detail-442894.html

到了这里,关于C++ 多线程:实现一个功能完整的线程池的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 一个简单的增删改查Spring boot项目教程(完整过程,附代码)(从搭建数据库到实现增删改查功能),Springboot学习,Springboot项目,

    这里将会介绍怎么去搭建一个简单增删改查的Springboot项目,认真看完我相信你一定能够学会,并且附有完整代码; 首先要进行增删改查肯定是要有供操作的数据库; 这里我是用的SQLyog来搭建的,随便用什么都可以,只要能确保给项目一个配套的数据库就行; 打开IDEA,创建

    2024年02月15日
    浏览(66)
  • C++ 手写一个线程池

    本专栏已在我的个人站点中完成更新升级,可点击这里直达。 本专栏不再更新,不要购买!如有需要,请前往我的自建站点中购买, 价格更实惠 、 内容更丰富 、 并且继续保持更新 。 已购买该专栏的同学,可点击这里查看后续调整方案。 更多说明,可点击这里查看。

    2024年02月13日
    浏览(44)
  • C++:设计一个线程安全的队列

    串行的程序只用到单个 CPU 核心, 希望加速整个程序, 考虑使用多线程加速。典型情况下可以找到生产者、消费者,两个角色之间通过队列进行数据交互: 生产者负责把数据放入队列Q 消费者负责从队列取出数据Q 要求队列是线程安全的,即:不能有读写冲突等。 这里并不给

    2024年02月14日
    浏览(37)
  • c++多线程按行读取同一个每行长度不规则文件

    对于非常大的比如上百G的大文件读取,单线程读是非常非常慢的,需要考虑用多线程读,多个线程读同一个文件时不用加锁的,每个线程打开一个独立的文件句柄 先打开一个文件句柄,获取整个文件大小 file_size 确定要采用线程读取的部分大小 read_size 和多线程的个数 thread_num ,算出

    2024年03月09日
    浏览(49)
  • C++类设计:一个比较复杂的日志类 支持多线程、文件切换、信息缓存(源码)

    初级代码游戏的专栏介绍与文章目录-CSDN博客 github位置:codetoys/ctfc.git src/env/myLog.h和.cpp           这个类功能细节比较多,因为是长期使用的版本,累积了各种功能。之前介绍的两个是我的测试代码用的版本,非常简单,那两个在这里: C++类设计:设计一个日志类(源码

    2024年04月10日
    浏览(41)
  • 使用PHP实现登录注册功能的完整指南

    🏆作者简介,黑夜开发者,全栈领域新星创作者✌,2023年6月csdn上海赛道top4。多年电商行业从业经验,对系统架构,数据分析处理等大规模应用场景有丰富经验。 🏆本文已收录于PHP专栏:PHP进阶实战教程。 🏆另有专栏PHP入门基础教程,希望各位大佬多多支持❤️。 PHP是一

    2024年02月15日
    浏览(54)
  • 一张表实现短视频“评论区“完整功能

    前言         现如今,不管是哪种类型的应用,评论区都少不了。从工具类的到媒体信息流类的,评论留言都是最基本的互动环节。比如抖音短视频下,针对视频每个用户都可以发表自己的观点;而针对用户的评论,其他的用户又可以对其进行评论,依次回复下去。      

    2024年02月16日
    浏览(91)
  • Linux C++ 网络编程基础(2) : TCP多线程一个server对应多个client

    作者:令狐掌门 技术交流QQ群:675120140 csdn博客:https://mingshiqiang.blog.csdn.net/   tcp编程时, 一个server可以对应多个client, server端用多线程可以实现. linux下多线程可以使用POSIX的线程函数, 下面给出服务端和客户端的代码.   Linux POSIX线程库提供了一组函数来创建、管理和同步

    2024年02月13日
    浏览(49)
  • 【C++】做一个飞机空战小游戏(四)——给游戏添加背景音乐(多线程技巧应用)

      [导读]本系列博文内容链接如下: 【C++】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值 【C++】做一个飞机空战小游戏(二)——利用getch()函数实现键盘控制单个字符移动 【C++】做一个飞机空战小游戏(三)——getch()函数控制任意造型飞机图标移动 【C++】做一个飞

    2024年02月14日
    浏览(45)
  • 【Spring Boot】Spring Boot实现完整论坛功能示例代码

    以下是一个简单的Spring Boot论坛系统示例代码: 首先是数据库设计,我们创建以下几张表: user表,存储用户信息,包括id、username、password、email、create_time等字段。 topic表,存储帖子信息,包括id、title、content、user_id、create_time等字段。 comment表,存储评论信息,包括id、con

    2024年02月11日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包