C++线程池-固定式线程池

一、概念
        固定式线程池是一个固定大小的线程池,在创建时会指定线程池中线程的数量。每当有任务提交到线程池时,线程池会启动一个线程来执行任务,直到达到线程池的最大线程数。

二、同步队列(缓冲区)
        同步队列为线程池中三层结构的中间层,主要作用是保证任务队列中,共享数据的线程安全,还为上一层同步服务层添加新任务的接口,以及为下一层异步服务层提供获取任务的接口。同时,还要限制任务数的上限,避免任务过多导致内存暴涨的问题。

        同步队列的实现,会用到C++11互斥锁、条件变量、右值引用、std::move以及完美转发。std::move是为了实现移动语义,std::forward是为了实现完美转发,关于右值引用、移动语义和完美转发。同步队列的锁是用来实现线程同步的,条件变量是用来实现线程间通信的,即线程池空了就要等待,不为空就通知一个线程去处理;线程池满了就等待,直到没有满的时候才通知上层添加任务。

        增加了Stop接口,以便让用户能终止任务,还做了进一步的改进,以提高性能。除了void Take(T& x)接口,每次获取到锁后,只能获取一个数据,其实这时队列中可能有多条数据,如果每条数据都加锁获取,效率是很低的,这里做出改进(用到了list容器),做到一次加锁就能将队列中所有数据都取出来,从而大大减少加锁的次数。在获取互斥锁之后,我们不再只获取一条数据,而是通过std::move来将队列中所有数据move到外面去,这样既大大减少了获取数据加锁的次数,又直接通过移动避免了数据的复制,提高了性能。

(代码都是自己实现,可以直接使用运行,测试用例放在最后面,仅供参考)

#ifndef SYNCQUEUE2_HPP//等待同步队列
#define SYNCQUEUE2_HPP
#include<iostream>
#include<list>
#include<mutex>;
#include<condition_variable>
#include<chrono>
using namespace std;

template<class Task>
class SyncQueue
{
private:
	std::list<Task>m_queue;
	std::mutex mux;
	std::condition_variable cv_x;//消费者
	std::condition_variable cv_s;//生产者
	std::condition_variable m_waitStop;//等待停止
	size_t maxsize;//任务上限
	bool m_stop;//false-->没停止(判断同步队列是否停止)
	size_t m_waitTime = 10;//10ms的等待时间

	bool IsFull()const { return m_queue.size() >= maxsize; }
	bool IsEmpty()const { return m_queue.empty(); }

	template<class F>
	int Add(F&& f)//添加到生产者
	{
		std::unique_lock<std::mutex>lock(mux);
		while (!m_stop && IsFull())
		{
			//cv_s.wait(lock);
			if (std::cv_status::timeout == cv_s.wait_for(lock, std::chrono::milliseconds(m_waitTime)))
			{
				return -1;
			}
		}
		//cv_s.wait(lock, [this]()->bool {return !m_stop && !IsFull() });

		if (m_stop)//同步队列停止
		{
			return -2;
		}
		m_queue.push_back(std::forward<F>(f));
		cv_x.notify_all();
		return 0;
	}
public:
	SyncQueue(size_t max) :maxsize(max), m_stop(false) {}
	~SyncQueue()
	{
	}
	void Stop()//强制停止-->不管同步队列有没有值都进行停止
	{
		{
			std::unique_lock<std::mutex>lock(mux);
			m_stop = true;
		}
		cv_x.notify_all();
		cv_s.notify_all();
	}
	void WaitStop()//等待停止-->等同步队列里的值都进行完了才进行停止
	{
		std::unique_lock<std::mutex>lock(mux);
		while (!IsEmpty())
		{
			//m_waitStop.wait(lock);
			m_waitStop.wait_for(lock, std::chrono::milliseconds(1));//等待1ms,超过1ms后自己就启动
		}
		m_stop = true;
		cv_x.notify_all();
		cv_s.notify_all();
	}
	int Put(Task&& task)//添加任务(生产者)
	{
		return Add(std::forward<Task>(task));
	}
	int Put(const Task& task)//添加任务(生产者)
	{
		return Add(task);
	}
	void Take(std::list<Task>& val)//消费者-->一次消费所有
	{
		std::unique_lock<std::mutex>lock(mux);
		while (!m_stop && IsEmpty())
		{
			cv_x.wait(lock);
		}
		if (m_stop)
		{
			return;
		}
		val = std::move(m_queue);
		cv_s.notify_all();
	}
	void Take(Task& val)//消费者-->一次消费一个
	{
		std::unique_lock<std::mutex>lock(mux);
		while (!m_stop && IsEmpty())
		{
			cv_x.wait(lock);
		}
		if (m_stop)
		{
			return;
		}
		val = m_queue.front();
		m_queue.pop_front();
		cv_s.notify_all();
	}
	bool Empty()const//判空
	{
		std::unique_lock<std::mutex>lock(mux);
		return m_queue.empty();
	}
	bool Full()const //判满
	{
		std::unique_lock<std::mutex>lock(mux);
		return m_queue.size() >= maxsize;
	}
	size_t Size()const //队列中元素个数
	{
		std::unique_lock<std::mutex>lock(mux);
		return m_queue.size();
	}
	size_t Count()const //队列中元素个数
	{
		return m_queue.size();
	}
};
#endif 

三、线程池的设计
        一个完整的线程池包括三层:同步服务层、排队层和异步服务层,其实这也是一种生产者-消费者模型,同步层是生产者,不断地将任务添加到排队层中,因此,线程池需要提供一个添加新任务的接口供生产者使用;消费者是异步层,具体是由线程池中预先创建的线程去处理排队层中的任务。排队层是一个同步队列,它内部保证了上下两层对共享数据的安全访问,同时还要保证队列不会被无限制地添加任务导致内存暴涨。另外,线程池还要提供一个停止的接口,让用户能够在需要的时候停止线程池的运行。

        ThreadPool中有3个成员变量,一个是线程组,这个线程组中的线程是预先创建的,应该创建多少个线程由外面传入,一般建议创建CPU核数的线程以达到最优的效率,线程组循环从同步队列中取出任务并执行,如果线程池为空,线程组将处于等待状态,等待任务的到来。另一个成员变量是同步队列,它不仅用来做线程同步,还用来限制同步队列的上限,这个上限也是由使用者设置的。第三个成员变量是用来停止线程池的,为了保证线程安全,我们用到原子变量atomic_bool。

#ifndef FIXED_THREAD_HPP//固定式线程池
#define FIXED_THREAD_HPP
#include"SyncQueue2.hpp"
#include<functional>
#include<memory>
#include<thread>
#include<atomic>//原子操作-->原子操作是指在多线程环境中,能够保证操作的完整性和一致性,不会被其他线程中断的操作。
#include<future>
using namespace std;

class FixedThreadPool
{
public:
	using Task = std::function<void(void)>;
private:
	SyncQueue<Task>m_queue;//同步队列
	std::list<std::unique_ptr<std::thread>>m_threadgroup;//放的是线程,由唯一性智能指针(unique_ptr)管理线程的生存期
	std::atomic_bool m_running;//当前线程是否处于运行状态(原子操作在多线程下安全)
	std::once_flag m_flag;//标识,运行一次的标记 // 确保某些操作(如停止线程)只执行一次

	void Start(int numthreads)//开始线程
	{
		m_running = true;
		for (int i = 0; i < numthreads; i++)
		{
			m_threadgroup.push_back(std::make_unique<std::thread>(&FixedThreadPool::RunningThread,this));//添加线程,RunningThread为要调用的函数,this为传进去的参数
		}
	}
	void RunningThread()//线程的入口函数
	{
		while (m_running)
		{
			Task task;
			m_queue.Take(task);
			if (task && m_running)
			{
				task();//调用函数
			}
		}
	}
	void stopThread()//停止线程
	{
		//m_queue.Stop();
		m_queue.WaitStop();
		m_running = false;
		for (auto& tha : m_threadgroup)
		{
			if (tha && tha->joinable())//joinable看线程是否还活动
			{
				tha->join();//等待线程结束
			}
		}
		m_threadgroup.clear();
	}
public:
	FixedThreadPool(size_t qusize = 100, int numthreads = 8) :m_queue(qusize), m_running(false) 
	{
		Start(numthreads);
	}
	~FixedThreadPool()
	{
		stop();
	}
	void stop()//结束任务
	{
		std::call_once(m_flag, [this]()->void {stopThread(); });//停止一次,call_once,看标志m_flag是否运行过一次,要是没有运行过,执行lambda表达式
	}

	template<class Func,class ... Args>//可变模板参
	auto AddTask(Func&& func, Args&& ... args)
	{
		using RetType = decltype(func(args...));//获取函数的返回值
		auto task = std::make_shared<std::packaged_task<RetType()>>(
			std::bind(std::forward<Func>(func), std::forward<Args>(args)...)
		);
		std::future<RetType>result = task->get_future();
		if (m_queue.Put([task] {(*task)();}) != 0)
		{
			(*task)();
		}
		return result;
	}
};
#endif

四.测试用例

#include"FixedThread.hpp"
#include<thread>
#include<vector>
#include<iostream>
#include<algorithm>
#include<chrono>
#include<future>
using namespace std;
using namespace std::chrono;
//------------------------排序测试
void my_rand(std::vector<int>& arr)
{
	int n = arr.capacity();
	for (int i = 0; i < n; i++)
	{
		arr.push_back(rand() % 1000000);
	}
}
void my_sort(std::vector<int>& arr)
{
	std::sort(arr.begin(), arr.end());
}
int main()
{
	FixedThreadPool mypool(20,8);//获得当前的逻辑处理器数

	const int n = 1000;
	const int elemnum = 10000;
	std::vector<std::vector<int>>arr;
	arr.resize(n);

	time_point<high_resolution_clock>begin;//高精度的时间点
	begin = high_resolution_clock::now();//得到当前时间
	{
		FixedThreadPool mypool(20,8);
		for (int i = 0; i < n; i++)
		{
			arr[i].reserve(elemnum);
			mypool.AddTask(std::bind(my_rand, std::ref(arr[i])));
		}
	}
	{
		FixedThreadPool mypool(20,8);
		for (int i = 0; i < n; i++)
		{
			mypool.AddTask(std::bind(my_sort, std::ref(arr[i])));
		}
	}
	std::int64_t diff = duration_cast<microseconds>(high_resolution_clock::now() - begin).count();//duration_cast<microseconds>强转为微妙,并cout数字化
	cout << " diff " << diff << " ms " << endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值