C++ 学习笔记实战写一个简单的线程池示例

 更新时间:2023年10月29日 15:57:16   作者:Totn  
这篇文章主要为大家介绍了C++实现一个简单的线程池学习实战,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

线程池的工作原理

  • 在构造函数中,创建指定数量的工作线程,并将它们存储在 workers 向量中。
  • 每个工作线程都通过无限循环来执行任务队列中的任务。
  • 设置一个public函数,接收新任务,封装为 std::function<void()> 并添加到任务队列中。
  • 等待的工作线程会被唤醒并获取任务执行,直到线程池被销毁或调用 stop() 函数停止。

第一步, 创建指定数量的工作线程

class ThreadPool {
public:
    ThreadPool(int num){
        // 启动指定数量的线程
        for(int i = 0; i < num; i++ ) {
            // 使用循环创建指定数量的工作线程。每个工作线程都是通过`emplace_back`函数向`workers`容器中添加一个lambda函数创建的
            // 添加一个lambda函数即为创建一个线程
            workers.emplace_back([this]{
                // code...
            });
      }
    }
private:
    // 用向量储存线程
    std::vector<std::thread> workers;
}

第二步在每个线程中启动死循环,依次从任务队列中取中任务,直到队列为空或设置stop标识为true;

第二步,在第一步基础上,添加代码

class ThreadPool {
public:
    ThreadPool(int num): stoped(false) {
        // 启动指定数量的线程
        for(int i = 0; i < num; i++ ) {
            // 使用循环创建指定数量的工作线程。每个工作线程都是通过`emplace_back`函数向`workers`容器中添加一个lambda函数创建的
            // 添加一个lambda函数即为创建一个线程
            workers.emplace_back([this]{
                // 死循环
                while(true) {
                    // 设置一个task接收从队列中取出的任务
                    std::function<void()> task;
                    {
                        // 从队列中取任务要加锁,以避免重复读
                        std::unique_lock<std::mutex> lock(ququeMutex);
                        // 如果stoped==true,或任务不为空,则使当前线程等待
                        condition.wait(lock, [this]{ return stroped || !tasks.empty(); });
                        // 设置stoped为true后,则等到任务都完成为退出(tasks任务队列为空)
                        if (stoped && tasks.empty()) {
                            return;
                        }
                        // 从队列中接收任务
                        task = std::move(tasks.front());
                        // 弹出已经接收的任务
                        tasks.pop();
                    }
                    // 以上代码使用{}限制了作用域,在域内启用的锁queueMutex在域结束后自动解锁
                }
            });
      }
    }
    // 设置停止标识
    void stop() {
        std::unique_lock<std::mutex> lock(queueMutex);
        stoped = true;
    }
private:
    // 用向量储存线程
    std::vector<std::thread> workers;
    // 工作队列
    std::queue<std::function<void()> tasks;
    // 队列互斥锁
    std::mutex queueMutex;
    // 条件变量
    std::condition_variable condition;
    // stop标识
    bool stoped;
}

第三步,设置一个函数用从外部接收任务

// class ThreadBool...
template<class F>
void enqueue(F&& f){
    {
        // 在域内启用锁, 保证加任务时没冲突
        std::unique_lock<std::mutex> lock(queueMutex);
        // 转发任务到tasks队列中
        tasks.emplace(std::forword<F>(f));
    }
    // 通知一个线程起来接活
    condition.notify_one();
}
// ...

第四步,设置析构函数等待所有线程执行完成

// class ThreadBool
~ThreadPool(){
 {
         std::unique_lock<std::mutex> lock(queueMutex);
      stoded = true;
     }
     // 阻塞等待所有线程
  for(std::thread& worker: workers){
      worker.join();
  }
}

最后完整代码

#include <vector>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <iostream>
thread_local int thr_num;
class Task{
    public run(int num = 0) {
        thr_num = num;
        std::cout << "当前任务号是" << thr_num << std::endl;
    }
}
class ThreadPool {
public:
    ThreadPool(int num): stoped(false) {
        // 启动指定数量的线程
        for(int i = 0; i < num; i++ ) {
            // 使用循环创建指定数量的工作线程。每个工作线程都是通过`emplace_back`函数向`workers`容器中添加一个lambda函数创建的
            // 添加一个lambda函数即为创建一个线程
            workers.emplace_back([this]{
                // 死循环
                while(true) {
                    // 设置一个task接收从队列中取出的任务
                    std::function<void()> task;
                    {
                        // 从队列中取任务要加锁,以避免重复读
                        std::unique_lock<std::mutex> lock(ququeMutex);
                        // 如果stoped==true,或任务不为空,则使当前线程等待
                        condition.wait(lock, [this]{ return stroped || !tasks.empty(); });
                        // 设置stoped为true后,则等到任务都完成为退出(tasks任务队列为空)
                        if (stoped && tasks.empty()) {
                            return;
                        }
                        // 从队列中接收任务
                        task = std::move(tasks.front());
                        // 弹出已经接收的任务
                        tasks.pop();
                    }
                    // 以上代码使用{}限制了作用域,在域内启用的锁queueMutex在域结束后自动解锁
                }
            });
      }
    }
    // 设置停止标识
    void stop() {
        std::unique_lock(std::mutex )
        stoped = true;
    }
private:
    // 用向量储存线程
    std::vector<std::thread> workers;
    // 工作队列
    std::queue<std::function<void()> tasks;
    // 队列互斥锁
    std::mutex queueMutex;
    // 条件变量
    std::condition_variable condition;
    // stop标识
    bool stoped;
}
int main() {
    ThreadPool tp(4);
    Task task;
    for(int i = 0; i<4; i++) {
        int num = i;
        tp.enqueue([num, &task] {
            task.run(num);
        });
    }
    return 0;
}

以上就是C++ 学习笔记实战写一个简单的线程池示例的详细内容,更多关于C++ 线程池的资料请关注脚本之家其它相关文章!

相关文章

  • 一文搞懂c++中的std::move函数

    一文搞懂c++中的std::move函数

    这篇文章主要介绍了c++中的std::move函数,在探讨c++11中的Move函数前,先介绍两个概念(左值和右值),对c++ std::move函数相关知识感兴趣的朋友一起看看吧
    2022-07-07
  • C语言 auto和register关键字

    C语言 auto和register关键字

    这篇文章主要介绍了C语言 auto、register关键字,文章通过变量展开全文相关的详细内容,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-04-04
  • C语言必背的一些经典程序代码实例

    C语言必背的一些经典程序代码实例

    C语言是一种高级编程语言,具有很多优点,下面这篇文章主要给大家介绍了关于C语言必背的一些经典程序代码,文中通过详细的实例代码介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • C++中拷贝构造函数的使用

    C++中拷贝构造函数的使用

    大家好,本篇文章主要讲的是C++中拷贝构造函数的使用,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-02-02
  • C++实现比较日期大小的示例代码

    C++实现比较日期大小的示例代码

    这篇文章主要为大家详细介绍了如何使用C++实现比较日期大小的功能,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的可以了解一下
    2023-04-04
  • C语言 完整游戏项目坦克大战详细代码

    C语言 完整游戏项目坦克大战详细代码

    《坦克大战》以二战坦克为题材,既保留了射击类游戏的操作性,也改进了射击类游戏太过于复杂难玩的高门槛特点,集休闲与竞技于一身。经典再度袭来,流畅的画面,疯狂的战斗,让玩家再次进入疯狂坦克的世界。玩家的目标是控制坦克躲避危险,消灭掉所有的敌人即可进入下一关
    2021-11-11
  • C++生成dll和调用dll的方法实例

    C++生成dll和调用dll的方法实例

    C++生成dll和调用dll的方法实例,需要的朋友可以参考一下
    2013-03-03
  • C语言入门的一些基本资源推荐和程序语法概览

    C语言入门的一些基本资源推荐和程序语法概览

    这篇文章主要介绍了C语言入门的一些基本资源推荐和程序语法概览,C语言是很多现代高级编程语言的基础,需要的朋友可以参考下
    2015-12-12
  • C/C++程序链接与反汇编工具objdump的使用介绍

    C/C++程序链接与反汇编工具objdump的使用介绍

    这篇文章主要介绍了C/C++程序链接与反汇编工具objdump的使用,程序构建过程的第二个阶段就是链接,链接过程输入的是目标文件的集合。每个目标文件可以被看作单个源代码文件的二进制存储版本
    2023-02-02
  • 利用Matlab制作三子棋游戏的示例代码

    利用Matlab制作三子棋游戏的示例代码

    三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。本文将用Matlab制作这一经典游戏,感兴趣的可以试一试
    2022-03-03

最新评论