当前位置: 首页 > news >正文

算法学习-线程池

目录

  • 多线程与线程池
  • 线程池的核心成员
  • 线程池案例
    • 1 运行环境以及目录
    • 2 `ThreadPool.h`
    • 3 `ThreadPool.cpp`
    • 4 main.cpp
    • 5、`CMakeLists.txt`
    • 7、运行与打印

多线程与线程池

  • 多线程:一种并发执行的技术,它允许在同一进程中同时执行多个线程。每个线程可以执行程序中的一部分代码,从而提高程序的执行效率。
  • 线程池:一种设计模式,用于管理和复用线程。线程池维护着多个线程,等待监督管理者分配可并行执行的任务。这样避免了在短时间内创建和销毁线程的代价。

线程池的核心成员

1、任务队列,任务队列中存放需要线程执行的任务;

2、互斥锁,由于任务队列中是临界资源,被多个线程访问,需要互斥锁保证安全性;

3、条件变量,当任务队列不为空的时候或者需要停止线程池运行时唤醒线程;

4、工作线程,负责不断从任务队列中取出任务并执行。

5、线程池是否停止工作的标志
运行逻辑如图所示:
在这里插入图片描述

线程池案例

1 运行环境以及目录

环境:Windows、Vscode、C++11

文件目录:
|—ThreadPool
|—main.cpp
|—ThreadPool.cpp
|—ThreadPool.h
|—CMakeLists.txt

2 ThreadPool.h

#define THREADPOOL_H

#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>   // std::function 提供了一种更通用的函数绑定和调用机制

class ThreadPool{
    public:
        ThreadPool(size_t numWorkers, size_t maxTasks);
        ~ThreadPool();

        void pushTask(std::function<void()> task);  // 添加任务
        void waitAll();  // 等待所有任务完成
    
    private:
        void workerLoop();  // 工作线程的循环

        std::vector<std::thread> m_workers;        // 工作线程
        std::queue<std::function<void()>> m_tasks;  // 任务队列

        std::mutex m_mutex;
        std::mutex m_print_mutex;
        std::condition_variable m_cond;
        std::condition_variable m_completionCond;

        bool m_stop = false;  // 线程池终止标志
        size_t m_maxTasks;    // 最大任务数
        size_t m_activeTasks = 0;  // 正在执行的任务数
};

#endif

3 ThreadPool.cpp

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

// 线程池构造函数
ThreadPool::ThreadPool(size_t numWorkers, size_t maxTasks): m_maxTasks(maxTasks) {
    for(size_t i = 0; i< numWorkers; ++i){
        m_workers.emplace_back(&ThreadPool::workerLoop, this);  // 创建工作线程并绑定到workerLoop函数上
        // 构造函数启动线程,但线程不会立刻执行,启动存在延迟,需要等待线程执行完才会输出
        // {
        //     std::lock_guard<std::mutex> lock(m_print_mutex); // 加锁,确保输出的顺序
        //     std::cout << "Thread " << i << " created." << "\tThread ID:"<< m_workers[i].get_id() << std::endl; // 输出线程创建信息
        // }
    }
}

// 线程池析构函数
ThreadPool::~ThreadPool(){
    {
        std::unique_lock<std::mutex> lock(m_mutex); // 加锁
        m_stop = true; // 设置终止标志
    }
    m_cond.notify_all(); // 通知所有等待的线程
    for(auto &worker : m_workers){  // 等待所有工作线程结束
        if(worker.joinable()){
            worker.join(); // 等待线程结束
        }
    }
}

// 向任务队列添加任务
void ThreadPool::pushTask(std::function<void()>task){
    std::unique_lock<std::mutex> lock(m_mutex); // 加锁
    m_cond.wait(lock, [this](){ return m_tasks.size() < m_maxTasks; }); // 等待任务队列有空位
    m_tasks.emplace(std::move(task)); // 添加任务到队列
    m_cond.notify_one(); // 通知一个等待的线程
}

// 线程循环函数
void ThreadPool::workerLoop(){
   while(true){
    std::function<void()> task; // 定义一个任务函数
    {
        std::unique_lock<std::mutex> lock(m_mutex); // 加锁
        m_cond.wait(lock, [this]{ return m_stop || !m_tasks.empty(); }); // 等待任务队列有任务或者线程池终止
        if(m_stop && m_tasks.empty()) return; // 如果线程池终止且任务队列为空,退出线程
        task = std::move(m_tasks.front()); // 获取任务,.front()返回队列的第一个元素的引用
        m_tasks.pop(); // 从队列中移除任务,.pop()移除队列的第一个元素
        m_activeTasks++; // 增加正在执行的任务数
    }
    task(); // 执行任务
    {
        std::unique_lock<std::mutex> lock(m_mutex); // 加锁
        m_activeTasks--; // 减少正在执行的任务数
        if(m_activeTasks == 0){ // 如果所有任务都执行完毕
            m_completionCond.notify_all(); // 通知所有等待的线程
        }
        // 当程序执行到该作用域的末尾时,lock对象会被销毁,自动释放互斥锁。
    }
   } 
}

// 等待所有任务完成
void ThreadPool::waitAll(){
    std::unique_lock<std::mutex> lock(m_mutex);
    m_completionCond.wait(lock, [this]{ return m_activeTasks == 0 && m_tasks.empty(); }); // 等待所有任务完成
}

4 main.cpp

#include "ThreadPool.h"
#include <iostream>
#include <chrono>
#include <mutex>

std::mutex mtx;  // 用于同步输出的互斥锁

void exampleTask(int taskId){
    {
        std::lock_guard<std::mutex> lock(mtx);  // 加锁,确保输出的顺序
        std::cout << "Task " << taskId << " started." << "\tThread ID is " << std::this_thread::get_id() << std::endl;
    }
    std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟任务执行时间
    {
        std::lock_guard<std::mutex> lock(mtx);
        std::cout << "Task " << taskId << " finished." << "\tThread ID is " << std::this_thread::get_id() << std::endl;
    }
}

int main(){
    const size_t numThreads = 4;  // 线程池中的线程数量
    const size_t numTasks = 10;   // 任务数量

    ThreadPool pool(numThreads, numTasks);  // 创建线程池

    // 向线程池添加任务
    for(int i = 0; i < numTasks; ++i){
        pool.pushTask(std::bind(exampleTask, i));  // 使用std::bind绑定任务函数和参数
        // pool.pushTask([i]{exampleTask(i);}); 
    }

    // 等待所有任务完成
    pool.waitAll();
    std::cout << "All tasks completed." << std::endl;

    return 0;
}   

5、CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(ThreadPoolV2)

# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 自动包含当前目录源文件
file(GLOB SOURCES CONFIGURE_DEPENDS
    "*.cpp"
    "*.h"
)

# 创建可执行目标
add_executable(threadpoolv2
    ${SOURCES}
)

# 多线程支持配置
if(CMAKE_CXX_COMPILE_ID MATCHES "GNU")
    # 针对MinGW的特殊设置
    set(THREADS_PREFER_PTHREAD_FLAG ON)
    find_package(Threads REQUIRED)
    target_link_libraries(threadpoolv2
        PRIVATE
        Threads::Threads
        -static  # 静态链接标准库
    )
endif()

6、编译过程

mkdir build
cd build
cmake .. -G "MinGW Makefiles"
cmake --build .

如果运行成功,则大概出现以下打印内容

[ 33%] Building CXX object CMakeFiles/threadpool.dir/ThreadPool.cpp.obj
[ 66%] Building CXX object CMakeFiles/threadpool.dir/main.cpp.obj
[100%] Linking CXX executable threadpool.exe
[100%] Built target threadpool

7、运行与打印

./threadpool	// 运行可执行程序

// 运行正确则打印如下内容:
Task 0 started. Thread ID is 5
Task 1 started. Thread ID is 3
Task 2 started. Thread ID is 2
Task 3 started. Thread ID is 4
Task 2 finished.        Thread ID is 2
Task 4 started. Thread ID is 2
Task 1 finished.        Thread ID is 3
Task 5 started. Thread ID is 3
Task 0 finished.        Thread ID is 5
Task 3 finished.        Thread ID is 4
Task 7 started. Thread ID is 4
Task 6 started. Thread ID is 5
Task 4 finished.        Thread ID is 2
Task 8 started. Thread ID is 2
Task 7 finished.        Thread ID is 4
Task 0 started. Thread ID is 5
Task 1 started. Thread ID is 3
Task 2 started. Thread ID is 2
Task 3 started. Thread ID is 4
Task 2 finished.        Thread ID is 2
Task 4 started. Thread ID is 2
Task 1 finished.        Thread ID is 3
Task 5 started. Thread ID is 3
Task 0 finished.        Thread ID is 5
Task 3 finished.        Thread ID is 4
Task 7 started. Thread ID is 4
Task 6 started. Thread ID is 5
Task 4 finished.        Thread ID is 2
Task 8 started. Thread ID is 2
Task 7 finished.        Thread ID is 4
Task 1 started. Thread ID is 3
Task 2 started. Thread ID is 2
Task 3 started. Thread ID is 4
Task 2 finished.        Thread ID is 2
Task 4 started. Thread ID is 2
Task 1 finished.        Thread ID is 3
Task 5 started. Thread ID is 3
Task 0 finished.        Thread ID is 5
Task 3 finished.        Thread ID is 4
Task 7 started. Thread ID is 4
Task 6 started. Thread ID is 5
Task 4 finished.        Thread ID is 2
Task 8 started. Thread ID is 2
Task 7 finished.        Thread ID is 4
Task 1 finished.        Thread ID is 3
Task 5 started. Thread ID is 3
Task 0 finished.        Thread ID is 5
Task 3 finished.        Thread ID is 4
Task 7 started. Thread ID is 4
Task 6 started. Thread ID is 5
Task 4 finished.        Thread ID is 2
Task 8 started. Thread ID is 2
Task 7 finished.        Thread ID is 4
Task 0 finished.        Thread ID is 5
Task 3 finished.        Thread ID is 4
Task 7 started. Thread ID is 4
Task 6 started. Thread ID is 5
Task 4 finished.        Thread ID is 2
Task 8 started. Thread ID is 2
Task 7 finished.        Thread ID is 4
Task 6 started. Thread ID is 5
Task 4 finished.        Thread ID is 2
Task 8 started. Thread ID is 2
Task 7 finished.        Thread ID is 4
Task 7 finished.        Thread ID is 4
Task 9 started. Thread ID is 4
Task 6 finished.        Thread ID is 5
Task 5 finished.        Thread ID is 3
Task 8 finished.        Thread ID is 2
Task 9 finished.        Thread ID is 4
All tasks completed.

相关文章:

  • 软考程序员-操作系统基本知识核心考点和知识重点总结
  • 代码随想录算法训练营第十四天|替换数字
  • 如果我没安装office,只安装了wps,python 如何通过win32com.client.Dispatch操作ppt?
  • 蓝桥杯备考:模拟题之神奇的幻方
  • 【nnUnetv2】推理+评估+测试
  • 计算机网络的分类及其性能指标
  • victoriametrics 部署
  • 【技术】外设驱动库开发笔记55:MAX31865热电阻变送器驱动
  • Pydantic Mixin:构建可组合的验证系统体系
  • Zstd(Zstandard)压缩算法
  • 数据库设计-笔记2
  • DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加导出数据功能示例9,TableView15_09带排序的导出表格示例
  • 多层感知机与反向传播
  • Qt调用Miniconda的python方法
  • 桥接模式 (Bridge Pattern)
  • Centos6配置yum源
  • 国企笔试之2025年中广核校招SHL测评笔试内容详解
  • 一文了解 threejs 中.bin 文件与 .gltf 文件 和 .glb 文件三者之间的关系
  • 汽车芯片成本控制:挑战、策略与未来趋势
  • 数学建模中的最大最小值模型详解
  • 长三角铁路今日预计发送旅客420万人次,有望创单日客发量新高
  • 2025五一档新片电影总票房破亿
  • 全文丨中华人民共和国传染病防治法
  • 聚焦各领域顶尖工匠,《上海工匠》第十季于五一播出
  • 五一“大车流”来了,今日午后G40沪陕高速开始迎来出沪高峰
  • 体坛联播|欧冠半决赛阿森纳主场不敌巴黎,北京男篮险胜山西