线程池(C++11)

what:什么是线程池?

维基百科解释:
线程池(英语:thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销。

why:为什么使用线程池?
目前的大多数网络服务器,包括Web服务器、Email服务器以及数据库服务器等都具有一个共同点,就是单位时间内必须处理数目巨大的连接请求,但处理时间却相对较短。
传统多线程方案中我们采用的服务器模型则是一旦接受到请求之后,即创建一个新的线程,由该线程执行任务。任务执行完毕后,线程退出,这就是是“即时创建,即时销毁”的策略。尽管与创建进程相比,创建线程的时间已经大大的缩短,但是如果提交给线程的任务是执行时间较短,而且执行次数极其频繁,那么服务器将处于不停的创建线程,销毁线程的状态。

线程池通常适合下面的几个场合:
(1)单位时间内处理任务频繁而且任务处理时间短
(2)对实时性要求较高。如果接受到任务后在创建线程,可能满足不了实时要求,因此必须采用线程池进行预创建。

how:怎样实现线程池?
一个线程队列,一个工作队列,每次取一个工作任务分配给一个线程去执行,循环到所有任务工作完成。

1、一个线程池pool,一个任务队列tasks;
2、任务队列是生产者-消费者模式,所以需要mutex+condition_variable,mutex就是锁,保证任务添加和移除的互斥性,condition_variable保证空队列时线程出于wait状态。
3、stop是atomic类型,用于控制线程池停止,由于是原子性操作,所以不需要额外加锁。

关于生产者和消费者模型,我之前写过一篇文章:c++多线程之条件变量及生产消费者模型,大家可以参考。

下面是一位大神写的代码,另一个大神给的注释,我这里贴一下,大家可以参考:
ThreadPool.h

#pragma once
#ifndef ThreadPool_h
#define ThreadPool_h

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

class ThreadPool {
public:
	ThreadPool(size_t);    //构造函数,size_t n 表示连接数

	template<class F, class... Args>
	auto enqueue(F&& f, Args&&... args)   //任务管道函数
		-> std::future<typename std::result_of<F(Args...)>::type>;  //利用尾置限定符  std future用来获取异步任务的结果

	~ThreadPool();
private:
	// need to keep track of threads so we can join them
	std::vector< std::thread > workers;   //追踪线程
	// the task queue
	std::queue< std::function<void()> > tasks;    //任务队列,用于存放没有处理的任务。提供缓冲机制

	// synchronization  同步?
	std::mutex queue_mutex;   //互斥锁
	std::condition_variable condition;   //条件变量?
	bool stop;
};

// the constructor just launches some amount of workers
inline ThreadPool::ThreadPool(size_t threads) : stop(false)
{
	for (size_t i = 0; i < threads; ++i)
		workers.emplace_back(     //以下为构造一个任务,即构造一个线程
			[this]
	{
		for (;;)
		{
			std::function<void()> task;   //线程中的函数对象
			{//大括号作用:临时变量的生存期,即控制lock的时间
				std::unique_lock<std::mutex> lock(this->queue_mutex);
				this->condition.wait(lock,
					[this] { return this->stop || !this->tasks.empty(); }); //当stop==false&&tasks.empty(),该线程被阻塞 !this->stop&&this->tasks.empty()
				if (this->stop && this->tasks.empty())
					return;
				task = std::move(this->tasks.front());
				this->tasks.pop();

			}

			task(); //调用函数,运行函数
		}
	}
	);
}

// add new work item to the pool
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args)  //&& 引用限定符,参数的右值引用,  此处表示参数传入一个函数
-> std::future<typename std::result_of<F(Args...)>::type>
{
	using return_type = typename std::result_of<F(Args...)>::type;
	//packaged_task是对任务的一个抽象,我们可以给其传递一个函数来完成其构造。之后将任务投递给任何线程去完成,通过
//packaged_task.get_future()方法获取的future来获取任务完成后的产出值
	auto task = std::make_shared<std::packaged_task<return_type()> >(  //指向F函数的智能指针
		std::bind(std::forward<F>(f), std::forward<Args>(args)...)  //传递函数进行构造
		);
	//future为期望,get_future获取任务完成后的产出值
	std::future<return_type> res = task->get_future();   //获取future对象,如果task的状态不为ready,会阻塞当前调用者
	{
		std::unique_lock<std::mutex> lock(queue_mutex);  //保持互斥性,避免多个线程同时运行一个任务

		// don't allow enqueueing after stopping the pool
		if (stop)
			throw std::runtime_error("enqueue on stopped ThreadPool");

		tasks.emplace([task]() { (*task)(); });  //将task投递给线程去完成,vector尾部压入
	}
	condition.notify_one();  //选择一个wait状态的线程进行唤醒,并使他获得对象上的锁来完成任务(即其他线程无法访问对象)
	return res;
}//notify_one不能保证获得锁的线程真正需要锁,并且因此可能产生死锁

// the destructor joins all threads
inline ThreadPool::~ThreadPool()
{
	{
		std::unique_lock<std::mutex> lock(queue_mutex);
		stop = true;
	}
	condition.notify_all();  //通知所有wait状态的线程竞争对象的控制权,唤醒所有线程执行
	for (std::thread &worker : workers)
		worker.join(); //因为线程都开始竞争了,所以一定会执行完,join可等待线程执行完
}
#endif /* ThreadPool_h */

main.cpp

#include <iostream>
#include "ThreadPool.h"

void func()
{
	std::this_thread::sleep_for(std::chrono::milliseconds(100));
	std::cout << "worker thread ID:" << std::this_thread::get_id() << std::endl;
}

int main()
{
	ThreadPool pool(4);
	while (1)
	{
		pool.enqueue(func);
	}

	system("pause");
	return 0;
}

结果:循环的运行着4个线程
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值