五分钟带你实现一个c++线程池

57 阅读2分钟
#include <vector>
#include <queue>
#include <iostream>
#include <thread>
#include <mutex>
#include <functional>
#include <condition_variable>
class ThreadPool
{

public:
    ThreadPool(int size) : size(size)
    {
        for (int i = 0; i < size; ++i)
        {
            threads.emplace_back([this]
                                 {
                while (true){
                    std::unique_lock<std::mutex> lock(_mutex);
                    condition.wait(lock, [this] { return !tasks.empty()||stop; });
                    if(tasks.empty()&&stop){
                        return;
                    }
                  std:: function<void()> task = std::move(tasks.front());
                  tasks.pop();
                  lock.unlock();
                  task();
                } });
        };
    }
    ~ThreadPool()
    {
        {
            std::unique_lock<std::mutex> lock(_mutex);
            stop = true;
        }
        condition.notify_all();
        for (auto &thread : threads)
        {
            thread.join();
        }
    }

    template <typename F, typename... Args>
    void add(F &&f, Args &&...args)
    {
        std::function<void()> task = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
        {
            std::unique_lock<std::mutex> lock(_mutex);
            tasks.emplace(move(task));
            condition.notify_one();
        }
    }

private:
    int size;
    std::vector<std::thread> threads;
    std::mutex _mutex;
    std::queue<std::function<void()>> tasks;
    std::condition_variable condition;
    bool stop = false;
};

int main()
{
    ThreadPool pool(4);
    for (int i = 0; i < 8; ++i)
    {
        pool.add([i]
                 { 
            std::cout << "Task " << i << " is running in thread " << std::this_thread::get_id() << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
            std::cout << "Task " << i << " is done" << std::endl; });
    }
    return 0;
}

`

ThreadPool线程池类

size记录开启多少个线程;

threads存放线程的数组;

任务由于是函数类型,我们并且采用队列方式存储,先入先出;

stop字段为是否终止线程;

构造函数:构造函数设置确定的线程数,并且去循环去获取任务,当任务队列已经没任务了或者线程开关被终止了,就需要进行条件等待,如果有的话就直接往下执行,task为函数用可调用对象包装器进行包装,类型为function<void()>取出第一个任务,然后执行task()方法;

析构函数:stop字段由于是线程都需要访问的字段,需要用锁,析构的时候需要通知所有等待的线程,把剩下的任务执行完毕;

成员函数add方法:往tasks队列新增任务的方法,两个参数均为万能引用,具体的类型,需要外部参数传递决定,Args &&...args为可变参数模版,不确定参数的数量,应用bind后,可将函数参数进行降次,因此task的函数类型可以确定为function<void()>类型;然后将task加入tasks任务队列,应用了move关键字,可直接将task指针转移给tasks队列,而不是拷贝出一份新的数据,这样可以增强性能;然后再去通知一个线程来获取任务;

大概的流程就是这样;但是本列子应用了许许多多的c嘎嘎11的新特性;如有不熟悉的,小伙伴们可以查漏补缺;

应用到一些知识如下

1.lambda表达式

2.unique_lock锁

3.condition_variable条件变量

4.emplace_back与push_back的区别

5.move转移与forward完美转发关键字

6.可调用类型包装器和bind的用法

7.thread线程类的基本用法

8.for循环里的自动推断类型auto的使用

9.左值右值的区别,T&&t类型的推断

10:互斥锁的使用,有兴趣的也可以去了解下原子变量atomic