使用C++11新特性实现线程池_对其中用到的部分特性详解

 先上总的代码,等会一一详解

环境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---------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值