C++线程池(固定线程数,条件变量通知,future返回)

参考资料:

  • 《C++并发编程实战》
  • https://en.cppreference.com

代码下载

https://github.com/541380000/csdn_share/tree/main/threadpool_cpp

实现(见注释)

#pragma once
#include <vector>
#include <queue>
#include <thread>
#include <atomic>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>
using std::thread;
using std::max;
using std::min;

class ThreadPool
{
    using Task = std::function<void()>;
    // 执行任务的线程
    std::vector<std::thread> pool;
    // 任务队列
    std::queue<Task> tasks;
    // 保护任务队列的锁
    std::mutex taskQueueLock;
    // 条件变量,所有线程在该变量上等待。有任务加入任务队列时,唤醒该条件变量
    std::condition_variable cvTask;
    // 是否已经关闭,关闭后,无法再提交任务
    std::atomic<bool> isStopped;
    // 空闲线程个数
    std::atomic<uint32_t> idleThreadNumber;
public:
    inline ThreadPool(uint32_t size = 4) : isStopped{ false }
    {
        idleThreadNumber = max(1U, size);
        idleThreadNumber = min(idleThreadNumber.load(), MAX_THREAD_NUMBER);
        for (size = 0; size < idleThreadNumber; ++size)
        {   // 初始化任务执行线程
            pool.emplace_back(
                    [this]
                    {
                        while (!this->isStopped)
                        {
                            std::function<void()> task;
                            {   // 在条件变量上等待
                                std::unique_lock<std::mutex> lock{ this->taskQueueLock };
                                // 等待,第二个参数Predicate,用于解决假唤醒,see also: https://en.cppreference.com/w/cpp/thread/condition_variable/wait
                                // 当线程池被终止,或者任务队列不为空的时候,不等待
                                // 相当于
                                /*
                                while (!(this->isStopped.load() || !this->tasks.empty()))
                                	this->cvTask.wait();
                                */
                                this->cvTask.wait(lock,
                                                   [this] {
                                                       return this->isStopped.load() || !this->tasks.empty();
                                                   }
                                ); // wait 直到有 task
                                // 线程池已关闭且没有任务可以做
                                if (this->isStopped && this->tasks.empty())
                                    return;
                                // 使用了notify_all后,多个线程被唤醒,但可能该线程没有拿到任务,那就继续等待
                                if (this->tasks.empty()) continue;
                                task = std::move(this->tasks.front()); // 取一個 task
                                this->tasks.pop();
                            }
                            idleThreadNumber--;
                            task();
                            idleThreadNumber++;
                        }
                    }
            );
        }
    }
    inline ~ThreadPool()
    {
        isStopped.store(true);
        cvTask.notify_all(); // 唤醒所有线程,执行任务
        for (std::thread& thread : pool) {
            if (thread.joinable())
                thread.join(); // 等待线程结束
        }
    }

public:
    const uint32_t MAX_THREAD_NUMBER = thread::hardware_concurrency();
    // 提交任务接口
    template<class F, class... Args>
    auto commit(F&& f, Args&&... args) ->std::future<decltype(f(args...))>
    {
        if (isStopped.load())    // stop == true ??
            throw std::runtime_error("commit on ThreadPool is stopped.");

        // 获取函数的返回值类型
        using RetType = decltype(f(args...));
        // 创建一个新的packaged_task,可以通过packaged_task获得future,进而在函数执行完成后获取返回值
        auto task = std::make_shared<std::packaged_task<RetType()> >(
                std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        std::future<RetType> future = task->get_future();
        {    // 将任务添加到任务队列
            std::lock_guard<std::mutex> lock{ taskQueueLock };
            //
            tasks.emplace(
                    [task]()
                    {
                        (*task)();
                    }
            );
        }
        // 唤醒一个线程进行执行
        cvTask.notify_one();
        return future;
    }
    [[nodiscard]] uint32_t GetIdleThreadNumber() const { return idleThreadNumber; }
    bool IsAllFinished(){
        std::lock_guard<std::mutex> lock{ taskQueueLock };
        return tasks.empty();
    }
};

如何使用示例

#include <iostream>
#include "threadpool.hpp"
using namespace std;

atomic_int64_t cnt = 0;
int64_t PrintHelloWorld(){
    return cnt++;
}

struct Number{
    bool isnan;
    double value;
    void operator()() const{
        if (isnan)
            cout << "nan\n";
        else {
            char buf[1024];
            sprintf(buf, "%lf\n", value);
            cout << string(buf);
        }
    }
};

int main() {
    ThreadPool pool(10);
    vector<decltype(pool.commit(PrintHelloWorld))> results1;
    // 相当于vector<future<int64_t>> results1;
    results1.reserve(50);
    // 存储future,然后调用get,get方法会阻塞直到future可用
    for (int i=0; i<50; i++){
        results1.emplace_back(pool.commit(PrintHelloWorld));
    }
    for (auto& r : results1){
        cout << r.get() << endl;
    }
    cout << "\n\n";
    for (int i=0; i<50; i++){
        pool.commit(Number{static_cast<bool>(rand() % 2), static_cast<double>(rand() % 200)});
    }
    while(!pool.IsAllFinished());
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(ThreadPool)

set(CMAKE_CXX_STANDARD 17)
# 需要链接pthread
find_package( Threads )
add_executable(ThreadPool main.cpp)
target_link_libraries( ThreadPool ${CMAKE_THREAD_LIBS_INIT} )
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
线程池是一种多线程处理方式,它可以通过预先创建一定量的线程,来处理多个任务。这样可以避免过多的线程创建和销毁开销,提高程序性能和稳定性。以下是一个简单的 C++ 线程池实现示例: ```cpp #include <iostream> #include <thread> #include <queue> #include <mutex> #include <condition_variable> class ThreadPool { public: ThreadPool(int threads) : stop(false) { for (int i = 0; i < threads; ++i) { workers.emplace_back([this] { for (;;) { std::function<void()> task; { std::unique_lock<std::mutex> lock(this->queue_mutex); 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(); } }); } } template<class F> void enqueue(F &&f) { { std::unique_lock<std::mutex> lock(queue_mutex); tasks.emplace(std::forward<F>(f)); } condition.notify_one(); } ~ThreadPool() { { std::unique_lock<std::mutex> lock(queue_mutex); stop = true; } condition.notify_all(); for (std::thread &worker: workers) worker.join(); } private: std::vector<std::thread> workers; std::queue<std::function<void()>> tasks; std::mutex queue_mutex; std::condition_variable condition; bool stop; }; ``` 在这个实现中,我们使用了一个 `std::queue` 来存储任务,使用了一个互斥锁 `std::mutex` 来保护队列,使用了一个条件变量 `std::condition_variable` 来实现线程等待和唤醒。通过 `enqueue` 方法向线程池中添加任务,通过 `stop` 标识来控制线程池的停止。 使用线程池的示例代码: ```cpp ThreadPool pool(4); std::vector<std::future<int>> results; for (int i = 0; i < 8; ++i) { results.emplace_back( pool.enqueue([i] { std::cout << "hello " << i << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "world " << i << std::endl; return i * i; }) ); } for (std::future<int> &result: results) std::cout << result.get() << ' '; std::cout << std::endl; ``` 在这个示例中,我们创建了一个线程池 `ThreadPool pool(4)`,然后向线程池中添加了 8 个任务。每个任务都是一个 lambda 表达式,其中包含了两个输出语句和一个返回值。通过 `std::future` 来获取每个任务的返回值。最后输出所有任务的返回值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值