首页 > 编程语言 >C++实现线程池详解

C++实现线程池详解

时间:2024-06-03 16:33:03浏览次数:23  
标签:std task lock C++ ThreadPool 详解 线程 include

在现代软件开发中,高效地管理和利用计算资源是一项关键任务。线程池(Thread Pool)是一种非常有效的并发编程技术,它允许我们管理和重用一组线程,从而避免频繁创建和销毁线程带来的性能开销。

1. 线程池的基本概念

线程池是一组预先创建的线程,这些线程等待并执行任务。当任务到达时,它们从任务队列中获取并执行。线程池的主要优点包括:

  1. 减少线程创建和销毁的开销:线程的创建和销毁是昂贵的操作,通过重用线程,可以显著减少这种开销。
  2. 提高响应速度:任务可以立即由线程池中的线程执行,而无需等待新线程的创建。
  3. 控制并发量:线程池可以限制并发执行的任务数量,从而避免系统资源的过度使用。

2. 线程池的基本组成

一个典型的线程池主要包含以下几个部分:

  1. 任务队列:存储待执行的任务。
  2. 工作线程:实际执行任务的线程。
  3. 任务调度:从任务队列中取出任务并分配给工作线程。

3. 线程池的实现

下面我们将一步步实现一个简单的线程池。

3.1 包含的头文件

首先,我们需要包含一些必要的头文件:

#include <iostream>
#include <vector>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>

3.2 任务队列

我们使用一个线程安全的任务队列来存储待执行的任务:

class ThreadPool {
public:
    using Task = std::function<void()>;

    ThreadPool(size_t numThreads);
    ~ThreadPool();

    template <class F, class... Args>
    auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>;

private:
    // Worker threads
    std::vector<std::thread> workers;

    // Task queue
    std::queue<Task> tasks;

    // Synchronization
    std::mutex queueMutex;
    std::condition_variable condition;
    bool stop;
};

// Constructor
ThreadPool::ThreadPool(size_t numThreads) : stop(false) {
    for (size_t i = 0; i < numThreads; ++i) {
        workers.emplace_back([this] {
            while(true) {
                Task task;
                {
                    std::unique_lock<std::mutex> lock(this->queueMutex);
                    this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });
                    if (this->stop && this->tasks.empty()) return;
                    task = std::move(this->tasks.front());
                    this->tasks.pop();
                }
                task();
            }
        });
    }
}

// Destructor
ThreadPool::~ThreadPool() {
    {
        std::unique_lock<std::mutex> lock(queueMutex);
        stop = true;
    }
    condition.notify_all();
    for (std::thread& worker : workers) {
        worker.join();
    }
}

3.3 添加任务到队列

我们需要一个方法来向线程池中添加任务。这个方法将返回一个 std::future 对象,允许我们获取任务的执行结果:

template <class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
    using returnType = typename std::result_of<F(Args...)>::type;

    auto task = std::make_shared<std::packaged_task<returnType()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));

    std::future<returnType> result = task->get_future();
    {
        std::unique_lock<std::mutex> lock(queueMutex);

        if (stop) throw std::runtime_error("enqueue on stopped ThreadPool");

        tasks.emplace([task]() { (*task)(); });
    }
    condition.notify_one();
    return result;
}

3.4 完整代码

将以上各部分代码整合在一起,形成一个完整的线程池实现:

#include <iostream>
#include <vector>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>

class ThreadPool {
public:
    using Task = std::function<void()>;

    ThreadPool(size_t numThreads);
    ~ThreadPool();

    template <class F, class... Args>
    auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>;

private:
    // Worker threads
    std::vector<std::thread> workers;

    // Task queue
    std::queue<Task> tasks;

    // Synchronization
    std::mutex queueMutex;
    std::condition_variable condition;
    bool stop;
};

// Constructor
ThreadPool::ThreadPool(size_t numThreads) : stop(false) {
    for (size_t i = 0; i < numThreads; ++i) {
        workers.emplace_back([this] {
            for (;;) {
                Task task;
                {
                    std::unique_lock<std::mutex> lock(this->queueMutex);
                    this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });
                    if (this->stop && this->tasks.empty()) return;
                    task = std::move(this->tasks.front());
                    this->tasks.pop();
                }
                task();
            }
        });
    }
}

// Destructor
ThreadPool::~ThreadPool() {
    {
        std::unique_lock<std::mutex> lock(queueMutex);
        stop = true;
    }
    condition.notify_all();
    for (std::thread& worker : workers) {
        worker.join();
    }
}

template <class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
    using returnType = typename std::result_of<F(Args...)>::type;

    auto task = std::make_shared<std::packaged_task<returnType()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));

    std::future<returnType> result = task->get_future();
    {
        std::unique_lock<std::mutex> lock(queueMutex);

        if (stop) throw std::runtime_error("enqueue on stopped ThreadPool");

        tasks.emplace([task]() { (*task)(); });
    }
    condition.notify_one();
    return result;
}

3.5 测试线程池

下面是一个简单的测试代码,展示如何使用这个线程池:

int main() {
    ThreadPool pool(4);

    auto result1 = pool.enqueue([](int answer) { return answer; }, 42);
    auto result2 = pool.enqueue([](int x, int y) { return x + y; }, 5, 3);

    std::cout << "Result1: " << result1.get() << std::endl;
    std::cout << "Result2: " << result2.get() << std::endl;

    return 0;
}

4. 结论

通过使用线程池,我们可以有效地管理并发任务,提高系统的性能和响应速度。

标签:std,task,lock,C++,ThreadPool,详解,线程,include
From: https://blog.csdn.net/m0_74091159/article/details/139418221

相关文章

  • DevOps生命周期的8个阶段和DevOps pipeline 详解
    您可能也在探索DevOpspipeline或工作流的概念,这些术语可能会根据不同的解释者而有所交替使用。尽管如此,DevOps生命周期和DevOpspipeline这两个术语更常被提及。本文将首先阐述DevOps生命周期的概念,然后深入介绍DevOpspipeline。DevOps生命周期和DevOpspipeline的概述DevOps......
  • C++实现进制转换工具
    下面是一个使用C++编写的简单数制转换工具,该工具包含了二进制、十进制和十六进制之间的转换功能。二进制转十进制intbinaryToDecimal(conststd::string&binary){intdecimal=0,base=1;intlen=binary.length();for(inti=len-1;i>=0;--i){......
  • Java多线程
    线程的定义Java线程是Java编程语言中的执行单元。在Java中,线程可以看作是轻量级的进程,它独立运行,具有自己的执行路径。线程的原理Java线程的实现基于操作系统的线程模型,但Java虚拟机(JVM)对线程的管理和调度做了封装和优化,使得Java线程更加可控和可靠。下面是Java线程的一些基本......
  • dotnet/.NET EF(Entity Framework)详解
    原文链接:https://upimg.baike.so.com/doc/6061191-6274247.html         https://blog.csdn.net/u013733643/article/details/123473628DOTNET就是.NET,严格说是:.NETFramework框架。但为什么叫DOTNET(.NET)呢?在计算机行业DOT是DistributedObjectTechnolo......
  • C++ primer plus习题及解析第七章(函数C++编程模块)
    题目:7.1编写一个程序,不断要求用户输入两个数,直到其中的一个为0。对于每两个数,程序将使用一个函数来计算它们的调和平均数,并将结果返回给main(),而后者将报告结果。调和平均数指的是倒数平均值的倒数,计算公式如下:调和平均数=2.0*x*y/(x+y)代码: intharmonic_averag......
  • 十大滤波(C++版)
    在翻阅了网上多个版本的滤波算法,发现很多仍停留在多年以前,很多版本的更替没有完成。自己和小伙伴研究了一下,研究成果如下,因为都是比较浅显的研究,如果有不符合常理的地方,请大家指出,一起进步。一、限幅滤波#include<iostream>#include<cmath>#include<vector>usingnamespa......
  • Swin-Transformer白话详解
    Swin-Transformer得益于其窗口注意力和偏移窗口注意力机制,平衡了感受野和计算效率,逐渐替代Vit成为了很多视觉网络的Backbone。下面将尽可能的清晰地解释其各个模块!参考文献nsformer网络结构详解文章目录1.SwinTransformer的创新点2.PatchPartition和LinearEmbed......
  • 【Java数据结构】详解Stack与Queue(一)
    ......
  • Content Security Policy 参数 值 详解
    例子:Content-Security-Policy:default-src'self';script-src'self'https://example.com;img-src'self'data:;style-src'self''unsafe-inline';font-src'self'https://example.com;这个CSP规......
  • c/c++设计模式---享元模式
    引入享元模式:围棋游戏:namespace_nmsp1{enumEnumColor//棋子类型{Black,//黑White//白};structPosition//棋子位置{intm_x;intm_y;Position(inttmpx,inttmpy):m_x(tmpx),m_y(tmpy){}......