#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