线程池


简单的线程池

在 C++11 中,你可以使用 std::thread 和 std::mutex 等标准库来实现一个简单的线程池。
这个代码定义了一个简单的线程池类 ThreadPool,可以用于提交任务并在多个线程上执行。
下面是代码的主要部分和功能:

  • ThreadPool 类:

    • 构造函数接受线程池大小作为参数。
    • addTask 方法用于向线程池添加任务。
    • ~ThreadPool 析构函数在销毁线程池时等待所有任务完成。
    • worker 函数是线程的执行函数,它从任务队列中获取任务并执行。
  • main 函数中,创建了一个大小为 4 的线程池。

    • 然后,通过调用 addTask 方法提交了 10 个任务到线程池。
    • 最后,创建了与线程池大小相同的线程,并启动它们来执行 worker 函数。

这个线程池的实现使用了条件变量 cv 和互斥锁 mtx 来同步任务的添加和线程的唤醒。当任务队列为空时,线程会被阻塞在 cv.wait 函数上,直到有任务可用。

以下是一个简单的示例代码:

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

std::mutex m;   // 互斥使用std::cout进行输出

class ThreadPool {
public:
    // 线程池大小
    size_t poolSize;
    // 任务队列
    std::queue<std::function<void()>> tasks;
    // 锁
    std::mutex mtx;
    // 条件变量
    std::condition_variable cv;
    // 线程数
    size_t numThreads;

public:
    ThreadPool(size_t poolSize) : poolSize(poolSize), numThreads(0) {}

    void addTask(const std::function<void()> &task) {
        std::unique_lock<std::mutex> lock(mtx);
        tasks.push(task);
        // 通知一个等待线程
        cv.notify_one();
    }

    ~ThreadPool() {
        // 等待所有任务n完成
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [=]{ return tasks.empty();});
    }

    void worker() {
        for(;;) {
            std::function<void()> task;

            {
                std::unique_lock<std::mutex> lock(mtx);
                // 等待任务
                cv.wait(lock, [=]{ return !tasks.empty();});
                task = tasks.front();
                tasks.pop();
            }
            // 执行任务
            task();

            // 停顿1秒,便于观察线程运行
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }
};

int main() {
    ThreadPool pool(4);

    for (int i = 0; i < 10; i++) {
        pool.addTask([=]{
            
            std::lock_guard<std::mutex> lck(m);
            std::cout << "Task " << i << " is running on thread " << std::this_thread::get_id() << std::endl;
        });
    }

    pool.addTask([=]{ 
        std::lock_guard<std::mutex> lck(m);
        std::cout << "All task are done " << std::this_thread::get_id() << std::endl;
    });

    std::vector<std::thread> threads;
    for (size_t i = 0; i < pool.poolSize; i++) {
        threads.push_back(std::thread(&ThreadPool::worker, &pool));
        /*
        具体来说,std::thread 函数需要一个函数指针作为参数,以及一些可选的参数。在这里,传递了 &pool 作为第二个参数,它是一个指向 ThreadPool 对象的指针。
        通过传递 &pool,新创建的线程将能够访问和操作 pool 对象的成员变量和方法。这样,多个线程可以同时执行 ThreadPool::worker 函数,并且通过共享 pool 对象来协作完成任务的分配和执行。
        传递地址的方式使得线程之间可以共享对同一个对象的访问,从而实现了线程池的并发执行和任务的分配。
        */
        pool.numThreads++;
    }

    for (auto &thread : threads) {
        thread.join();
    }

    return 0;
}

/*
Task 0 is running on thread 140543688439488
Task 1 is running on thread 140543671654080
Task 2 is running on thread 140543680046784
Task 3 is running on thread 140543543736000
Task 4 is running on thread 140543543736000
Task 6 is running on thread 140543680046784
Task 5 is running on thread 140543688439488
Task 7 is running on thread 140543671654080
Task 8 is running on thread 140543688439488
Task 9 is running on thread 140543671654080
All task are done 140543543736000
*/

在上面的示例中, ThreadPool 类有以下几个关键部分:

  • poolSize 表示线程池的大小。
  • tasks 是一个任务队列,使用 std::queue 实现。
  • mtx 是一个互斥锁,用于保护任务队列的访问。
  • cv 是一个条件变量,用于通知等待的线程有任务可用。
  • numThreads 表示当前运行的线程数。

在 addTask 方法中,使用 std::unique_lock 来获取互斥锁,并将任务添加到任务队列中。然后,使用 cv.notify_one 通知一个等待的线程。
在 worker 方法中,使用循环不断等待任务。在循环中,使用 std::unique_lock 来获取互斥锁,并使用 cv.wait 等待条件变量的信号。当有任务可用时,从队列中取出任务并执行。
在 main 函数中,创建一个线程池,并向其添加多个任务。最后,启动所有的线程,并等待所有任务完成。
请注意,这只是一个简单的线程池实现示例,可能不适用于所有情况。在实际应用中,你可能需要根据具体需求进行调整和优化。


C++ 线程池 (学习)

https://xie.infoq.cn/article/a7f14675d4d7f4e9f386303ab

posted @ 2024-01-22 19:17  guanyubo  阅读(6)  评论(0编辑  收藏  举报