先上总的代码,等会一一详解
环境VS2017
Thread_Pool.h
#pragma once
#ifndef Thread_Poolh
#define Thread_Poolh
#include <vector>
#include <future>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <utility>
#include <thread>
#include <memory>
#include <functional>
class Thread_Pool
{
public:
inline Thread_Pool(int thread_size) : stop(false)/*stop是一个停止的变量,如果线程池被析构的时候,stop变为true*/
{
for (int i = 0; i < thread_size; i++)
works.emplace_back([this] {/*往线程池里面使用lamuda表达式创建线程,传入外部参数this*/
while (true)
{
std::function<void()> task;/*创建一个函数变量,存储的是一个函数,void()形式的,但其实是lamuda表达式*/
{//进入临界区域使用大括号,调用了unique_lock,会在出临界区域的时候,释放
std::unique_lock<std::mutex> lock(this->quenue_mutex);/*使用类中的mutex初始化,加上锁*/
this->condition.wait(lock, [this] {/*先拿上锁,等待一个消息,notify_one or notify_all。先看后面这个参数是否是false,是false,则把锁释放,阻塞自己。*/
return this->stop || !this->tasks.empty();
});
if (this->stop && this->tasks.empty())/*当函数析构,并且消息队列里面的任务执行完了,就返回*/
return;
task = std::move(this->tasks.front());/*使用完美转发,把函数地址参数等等传递给task*/
this->tasks.pop();
}
task();//执行任务,然后继续循环
}
});
}
template<typename F, class...Args>//使用传递变长参数的形式
auto tash_queue_push_back(F&& f, Args&& ...args) -> std::future<typename std::result_of<F(Args...)>::type>/*这里使用了返回值后置的写法,因为一开始不知道返回值的类型是什么,使用future,*/
{ //必须使用typeame,让编译器知道这是一个变量,然后用type方法获得类型
using return_type = typename std::result_of<F(Args...)>::type;//C++11使用的区别名的方式
auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));/*首先使用make_share创建一个变量,这个变量的形式是return_type()*/
std::future<return_type> res = task->get_future(); //这种形式,使用packaged打包在一个,使用forward完美转发,再使用bind把函数和参数绑定在一起
{
std::unique_lock<std::mutex> lock(quenue_mutex);//加锁
if(stop)
throw std::runtime_error("enqueue on stopped ThreadPool");
this->tasks.emplace([task]{(*task)();});//往任务队列加元素
}
condition.notify_one();//唤醒一个阻塞的线程
return res;
}
~Thread_Pool()
{
{
std::unique_lock<std::mutex> lock(quenue_mutex);/*析构时,进入临界区域,把stop置为true*/
stop = true;
}
condition.notify_all();//唤醒所有线程,加大马力干活
for (std::thread &work : works)
work.join();//使所有线程执行完后,才退出
}
private:
std::vector<std::thread> works;//装线程的容器
std::queue<std::function<void()>> tasks;//装任务的队列
bool stop;//析构时的标志变量
std::mutex quenue_mutex;//锁
std::condition_variable condition;//环境变量
};
#endif /* Thread_Poolh */
main.cpp
#include <iostream> // std::cout, std::endl
#include <vector> // std::vector
#include <string> // std::string
#include <future> // std::future
#include <thread> // std::this_thread::sleep_for
#include <chrono> // std::chrono::seconds
#include "my_thread_pool.h"
int main()
{
Thread_Pool pool(4);
std::vector< std::future<std::string> > results;
for (int i = 0; i < 8; ++i) {
results.emplace_back(
pool.tash_queue_push_back([i] {
std::cout << "hello " << i << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "world " << i << std::endl;
return std::string("---thread ") + std::to_string(i) + std::string(" finished.---");
})
);
}
for (auto& res : results)
std::cout << res.get() << std::endl;
}
1、不使用using namespace std;防止命名冲突,建议工程不使用。
2、构造函数手动为inline,emplace_back(),和右值引用。
inline Thread_Pool(int thread_size) : stop(false)
{
for (int i = 0; i < thread_size; i++)
works.emplace_back([this] {//特性一 lambda 表达式
while (true)
{
std::function<void()> task;//特性二function
//下面为临接区
{
std::unique_lock<std::mutex> lock(this->quenue_mutex);//特性三unique_lock
this->condition.wait(lock, [this] {//特性四condition
return this->stop || !this->tasks.empty();//这里必须使用!this->tasks.empty(), 注意!的原来
});
if (this->stop && this->tasks.empty())
return;
task = std::move(this->tasks.front());
this->tasks.pop();
}
task();
}
});
}
首先创造thread_size个线程 for (int i = 0; i < thread_size; i++)
这句话后面跟了一条语句works.emplace_back();
每往里面丢一个,就自动创建一个线程。
emplace_back()这个函数与push_back()的不同,引入一个又值引用的时候,不会创建一个临时变量。
那什么是右值引用呢?
简单来说,当我们
int a = 10;
a是一个左值变量, 10是一个右值。
之前C++98/03不允许传入右值(好像是,有兴趣可以去考证233)
现在如果我们向函数传入右值的时候,可以直接传地址,可以节省构造的时间和空间。
再举个例子:交换两个数字 a = 10, b = 20
int main()
{
int temp = std::move(a);//使右值传递给temp
a = b;
b = temp;
}
后面还用到了std::forward(),这个就后面类似srd::move()
这里右值说完了,回到前面的一条语句那个地方。
我知道你们想说什么 一条语句就是为什么那么长!!!
因为有lambda 表达式的原因
好了,lambda表达来了。
直观感受就是匿名,方便的命名的函数
Lambda表达式完整的声明格式如下:
[capture list] (params list) mutable exception-> return type { function body }
---------------------------------------今天累了,明天再写2018.12.18 23:19---------------------