使用C++一步一步实现线程池


步骤 1:理解线程池的基本概念

线程池是一种多线程编程模式,在程序启动时创建一组线程,并将其放入一个“池”中。任务被提交到线程池后,空闲线程会从任务队列中取出任务并执行。这样可以避免频繁创建和销毁线程的开销,提高性能。

线程池的核心组件包括:

  • 线程池管理器:管理线程的创建和销毁。
  • 工作线程:执行任务的线程。
  • 任务队列:存储待执行的任务。
  • 同步机制:如互斥锁和条件变量,确保线程安全。

步骤 2:设计线程池的接口

我们将实现一个 ThreadPool 类,提供以下功能:

  • 构造函数:指定线程数量。
  • 析构函数:清理线程池。
  • 任务提交函数:将任务加入队列。

我们将使用模板支持各种任务类型(如函数、lambda 表达式等)。


步骤 3:实现线程安全的任务队列

任务队列需要支持多线程访问,因此我们使用 std::queue 并结合 std::mutexstd::condition_variable 实现线程安全。


步骤 4:实现线程池类

线程池类将包含任务队列和工作线程,并提供任务提交接口。


步骤 5:编写基础完整线程池代码

下面是完整的基础完整线程池实现代码,包含所有必要的组件。我们将逐步解释每个部分。

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

// 线程安全的任务队列
template <typename T>
class ThreadSafeQueue {
public:
    void push(T task) {
        std::lock_guard<std::mutex> lock(mutex_);
        queue_.push(std::move(task));
        cond_.notify_one();
    }

    void wait_and_pop(T& task) {
        std::unique_lock<std::mutex> lock(mutex_);
        cond_.wait(lock, [this]{ return !queue_.empty(); });
        task = std::move(queue_.front());
        queue_.pop();
    }

    bool empty() const {
        std::lock_guard<std::mutex> lock(mutex_);
        return queue_.empty();
    }

    void notify_all() {
        cond_.notify_all();
    }

private:
    mutable std::mutex mutex_;
    std::queue<T> queue_;
    std::condition_variable cond_;
};

// 线程池类
class ThreadPool {
public:
    explicit ThreadPool(size_t num_threads) {
        for (size_t i = 0; i < num_threads; ++i) {
            workers_.emplace_back(&ThreadPool::worker_thread, this);
        }
    }

    ~ThreadPool() {
        stop_ = true;           // 设置停止标记
        tasks_.notify_all();    // 向所有等待的线程发送通知
        for (auto& worker : workers_) {
            if (worker.joinable()) {
                worker.join();  // 等待所有线程结束
            }
        }
    }

    template <typename F, typename... Args>
    auto enqueue(F&& f, Args&&... args) -> std::future<decltype(f(args...))> {
        using return_type = decltype(f(args...));
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        std::future<return_type> future = task->get_future();
        tasks_.push([task]() { (*task)(); });
        return future;
    }

private:
    void worker_thread() {
        while (!stop_) {
            std::function<void()> task;
            tasks_.wait_and_pop(task);
            if (task) {
                task();
            }
        }
    }

    std::vector<std::thread> workers_;
    ThreadSafeQueue<std::function<void()>> tasks_;
    std::atomic<bool> stop_{false};
};

步骤 6:测试线程池

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

#include <iostream>

// 测试代码
int main() {
    ThreadPool pool(4);  // 创建一个包含 4 个线程的线程池
    std::vector<std::future<void>> futures;

    // 提交 8 个任务
    for (int i = 0; i < 8; ++i) {
        auto future = pool.enqueue([i] {
            std::cout << "Task " << i << " is running" << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
            std::cout << "Task " << i << " is done" << std::endl;
        });
        futures.push_back(std::move(future));
    }

    // 等待所有任务完成
    for (auto& future : futures) {
        future.wait();
    }

    return 0;
}

运行这段代码,你会看到 8 个任务被 4 个线程并发执行,每个任务休眠 1 秒后完成。


代码说明

  1. 任务队列 (ThreadSafeQueue)

    • push:添加任务并通知等待的线程。
    • try_pop:尝试取出任务,用于非阻塞检查。
    • wait_and_pop:阻塞等待任务,用于线程空闲时等待。
    • 使用 std::mutexstd::condition_variable 确保线程安全。
  2. 线程池 (ThreadPool)

    • 构造函数创建指定数量的线程。
    • 每个线程运行 worker_thread,从队列中取出任务并执行。
    • enqueue 使用模板和 std::bind 支持灵活的任务提交。
    • 析构函数设置停止标志并等待所有线程结束。
  3. 同步与优化

    • 使用 std::atomic<bool> 确保停止标志的线程安全。
    • 线程在队列为空时调用 yield(),避免忙等待。

步骤 7:可以进行的改进

下面,我将通过改进以上的 ThreadPool 来满足以下功能,改进内容包括:

  1. 任务返回值支持(通过 std::future 实现)。
  2. 动态线程数调整(添加用于增加/减少线程的方法)。
  3. 任务优先级队列(用基于优先级的队列取代标准队列)。
    原始代码已经支持任务返回值,所以我将专注于添加另外两个功能,同时保留现有功能。我将提供一个完整的、增强的实现,然后附上解释和测试示例。
#include <queue>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <thread>
#include <atomic>
#include <functional>
#include <future>
#include <iostream>
#include <chrono>

// Task wrapper with priority
struct Task {
    std::function<void()> func;
    int priority;
    Task(std::function<void()> f, int p) : func(std::move(f)), priority(p) {}
    // For priority queue: higher priority tasks come first
    bool operator<(const Task& other) const {
        return priority < other.priority;
    }
};

// Thread-safe priority queue
class ThreadSafePriorityQueue {
public:
    void push(Task task) {
        std::lock_guard<std::mutex> lock(mutex_);
        queue_.push(std::move(task));
        cond_.notify_one();
    }

    bool wait_and_pop(Task& task) {
        std::unique_lock<std::mutex> lock(mutex_);
        cond_.wait(lock, [this] { return !queue_.empty(); });
        task = std::move(queue_.top());
        queue_.pop();
        return true;
    }

    bool empty() const {
        std::lock_guard<std::mutex> lock(mutex_);
        return queue_.empty();
    }

    void notify_all() {
        cond_.notify_all();
    }

private:
    mutable std::mutex mutex_;
    std::priority_queue<Task> queue_;
    std::condition_variable cond_;
};

// Enhanced ThreadPool class
class ThreadPool {
public:
    explicit ThreadPool(size_t num_threads) : stop_(false) {
        for (size_t i = 0; i < num_threads; ++i) {
            workers_.emplace_back(&ThreadPool::worker_thread, this);
        }
    }

    ~ThreadPool() {
        stop_ = true;
        tasks_.notify_all();
        for (auto& worker : workers_) {
            if (worker.joinable()) {
                worker.join();
            }
        }
    }

    // Enqueue task with priority
    template <typename F, typename... Args>
    auto enqueue(int priority, F&& f, Args&&... args) -> std::future<decltype(f(args...))> {
        using return_type = decltype(f(args...));
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        std::future<return_type> future = task->get_future();
        tasks_.push(Task([task]() { (*task)(); }, priority));
        return future;
    }

    // Add threads dynamically
    void add_threads(size_t n) {
        std::lock_guard<std::mutex> lock(pool_mutex_);
        for (size_t i = 0; i < n; ++i) {
            workers_.emplace_back(&ThreadPool::worker_thread, this);
        }
    }

    // Remove threads dynamically
    void remove_threads(size_t n) {
        std::lock_guard<std::mutex> lock(pool_mutex_);
        if (n >= workers_.size()) {
            n = workers_.size() - 1; // Ensure at least one thread remains
        }
        stop_ = true; // Temporarily stop to wake threads
        tasks_.notify_all();
        for (size_t i = 0; i < n; ++i) {
            if (workers_[i].joinable()) {
                workers_[i].join();
            }
        }
        workers_.erase(workers_.begin(), workers_.begin() + n);
        stop_ = false; // Resume normal operation
    }

private:
    void worker_thread() {
        while (!stop_) {
            Task task(nullptr, 0);
            if (tasks_.wait_and_pop(task)) {
                if (task.func) {
                    task.func();
                }
            }
        }
    }

    std::vector<std::thread> workers_;
    ThreadSafePriorityQueue tasks_;
    std::atomic<bool> stop_;
    std::mutex pool_mutex_; // Protects thread pool state
};

// Test code
int main() {
    ThreadPool pool(2); // Start with 2 threads
    std::vector<std::future<int>> futures;

    // Submit tasks with varying priorities
    for (int i = 0; i < 6; ++i) {
        int priority = (i % 2 == 0) ? 10 : 1; // Even tasks: high priority (10), odd: low (1)
        auto future = pool.enqueue(priority, [i]() {
            std::cout << "Task " << i << " (priority " << (i % 2 == 0 ? 10 : 1) << ") running\n";
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            return i * 10;
        });
        futures.push_back(std::move(future));
    }

    // Dynamically add 2 more threads
    std::cout << "Adding 2 threads...\n";
    pool.add_threads(2);

    // Submit more tasks
    for (int i = 6; i < 10; ++i) {
        int priority = (i % 2 == 0) ? 10 : 1;
        auto future = pool.enqueue(priority, [i]() {
            std::cout << "Task " << i << " (priority " << (i % 2 == 0 ? 10 : 1) << ") running\n";
            std::this_thread::sleep_for(std::chrono::milliseconds(500));
            return i * 10;
        });
        futures.push_back(std::move(future));
    }

    // Retrieve results
    for (size_t i = 0; i < futures.size(); ++i) {
        std::cout << "Result of task " << i << ": " << futures[i].get() << "\n";
    }

    // Remove 1 thread
    std::cout << "Removing 1 thread...\n";
    pool.remove_threads(1);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值