头图

线程池的工作原理大概如下:

  • 在构造函数中,创建指定数量的工作线程,并将它们存储在 workers 向量中。
  • 每个工作线程都通过无限循环来执行任务队列中的任务。
  • 设置一个public函数,接收新任务,封装为 std::function<void()> 并添加到任务队列中。
  • 等待的工作线程会被唤醒并获取任务执行,直到线程池被销毁或调用 stop() 函数停止。

第一步, 创建指定数量的工作线程:


class ThreadPool {
public:
    ThreadPool(int num){
        // 启动指定数量的线程
        for(int i = 0; i < num; i++ ) {
            // 使用循环创建指定数量的工作线程。每个工作线程都是通过`emplace_back`函数向`workers`容器中添加一个lambda函数创建的
            // 添加一个lambda函数即为创建一个线程
            workers.emplace_back([this]{
                // code...
            });
      }
    }
    
private:
    // 用向量储存线程
    std::vector<std::thread> workers;
    
}

第二步,在每个线程中启动死循环,依次从任务队列中取中任务,直到队列为空或设置stop标识为true;
在第一步基础上,添加代码如下

class ThreadPool {
public:
    ThreadPool(int num): stoped(false) {
        // 启动指定数量的线程
        for(int i = 0; i < num; i++ ) {
            // 使用循环创建指定数量的工作线程。每个工作线程都是通过`emplace_back`函数向`workers`容器中添加一个lambda函数创建的
            // 添加一个lambda函数即为创建一个线程
            workers.emplace_back([this]{
                // 死循环
                while(true) {
                    // 设置一个task接收从队列中取出的任务
                    std::function<void()> task;
                    {
                        // 从队列中取任务要加锁,以避免重复读
                        std::unique_lock<std::mutex> lock(ququeMutex);
                        // 如果stoped==true,或任务不为空,则使当前线程等待
                        condition.wait(lock, [this]{ return stroped || !tasks.empty(); });
                        // 设置stoped为true后,则等到任务都完成为退出(tasks任务队列为空)
                        if (stoped && tasks.empty()) {
                            return;
                        }
                        // 从队列中接收任务
                        task = std::move(tasks.front());
                        // 弹出已经接收的任务
                        tasks.pop();
                    }
                    // 以上代码使用{}限制了作用域,在域内启用的锁queueMutex在域结束后自动解锁
                }
            });
      }
    }
    
    // 设置停止标识
    void stop() {
        std::unique_lock<std::mutex> lock(queueMutex);
        stoped = true;
    }
private:
    // 用向量储存线程
    std::vector<std::thread> workers;
    // 工作队列
    std::queue<std::function<void()> tasks;
    // 队列互斥锁
    std::mutex queueMutex;
    // 条件变量
    std::condition_variable condition;
    // stop标识
    bool stoped;
}

第三步,设置一个函数用从外部接收任务

// class ThreadBool...
template<class F>
void enqueue(F&& f){
    {
        // 在域内启用锁, 保证加任务时没冲突
        std::unique_lock<std::mutex> lock(queueMutex);
        // 转发任务到tasks队列中
        tasks.emplace(std::forword<F>(f));
    }
    // 通知一个线程起来接活
    condition.notify_one();
    
}

// ...

第四步:设置析构函数等待所有线程执行完成

// class ThreadBool
~ThreadPool(){
 {
         std::unique_lock<std::mutex> lock(queueMutex);
      stoded = true;
     }
     // 阻塞等待所有线程
  for(std::thread& worker: workers){
      worker.join();
  }

}

最后完整代码如下:

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

thread_local int thr_num;

class Task{
    public run(int num = 0) {
        thr_num = num;
        std::cout << "当前任务号是" << thr_num << std::endl;
    }
}
class ThreadPool {
public:
    ThreadPool(int num): stoped(false) {
        // 启动指定数量的线程
        for(int i = 0; i < num; i++ ) {
            // 使用循环创建指定数量的工作线程。每个工作线程都是通过`emplace_back`函数向`workers`容器中添加一个lambda函数创建的
            // 添加一个lambda函数即为创建一个线程
            workers.emplace_back([this]{
                // 死循环
                while(true) {
                    // 设置一个task接收从队列中取出的任务
                    std::function<void()> task;
                    {
                        // 从队列中取任务要加锁,以避免重复读
                        std::unique_lock<std::mutex> lock(ququeMutex);
                        // 如果stoped==true,或任务不为空,则使当前线程等待
                        condition.wait(lock, [this]{ return stroped || !tasks.empty(); });
                        // 设置stoped为true后,则等到任务都完成为退出(tasks任务队列为空)
                        if (stoped && tasks.empty()) {
                            return;
                        }
                        // 从队列中接收任务
                        task = std::move(tasks.front());
                        // 弹出已经接收的任务
                        tasks.pop();
                    }
                    // 以上代码使用{}限制了作用域,在域内启用的锁queueMutex在域结束后自动解锁
                }
            });
      }
    }
    
    // 设置停止标识
    void stop() {
        std::unique_lock(std::mutex )
        stoped = true;
    }
private:
    // 用向量储存线程
    std::vector<std::thread> workers;
    // 工作队列
    std::queue<std::function<void()> tasks;
    // 队列互斥锁
    std::mutex queueMutex;
    // 条件变量
    std::condition_variable condition;
    // stop标识
    bool stoped;
}

int main() {
    ThreadPool tp(4);
    
    Task task;
    for(int i = 0; i<4; i++) {
        int num = i;
        tp.enqueue([num, &task] {
            task.run(num);
        });
    }

    return 0;
}

Totn
10 声望5 粉丝

幼稚也没关系,去实践,去经历,去思考!