算法学习-线程池

多线程与线程池

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

线程池的核心成员

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.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值