C++线程池-工作窃取式线程池

一、概述
        工作窃取式线程池采用了工作窃取算法,具体来说就是当某个线程执行完自己队列中的任务后,会从其他线程的队列中“偷取”任务来执行。这种算法可以提高线程利用率,减少线程之间的竞争,以及减小线程的等待时间。

        在同步队列中设计std::vector<std::list<T>>,使用该容器来存储任务,利用数组加链表,设置vector的大小为bucketsize,即一般为CPU核数,利用链表存放具体任务。当index下标的list容器中任务处理结束,则使用threadIndex生成bucketSize范围内不为index下标的new_index,然后线程可以去处理该list容器中的任务。

        设置m_waitTime,即为当任务缓冲区中满或者空时,生产者和消费者的等待时间(在此利用条件变量->wait_for)当发生超时,则生产者和消费者退出。在线程池中存在异步运行策略,即当添加任务失败时,可以先执行此任务。任务使用function和bind结合的形式进行传递,即using task=std::function<void(void)>,bind绑定任务函数名以及参数,通过function可调用对象包装器执行。

        WorkStealingPool可以设定多个工作线程,每个工作线程都有一个自己的任务队列,每个线程在执行任务时会首先从自己的队列中获取任务,如果自己队列为空,则从其他线程的队列中获取任务。这种设计可以充分发挥多核处理器的并行能力,提高整体的任务处理效率。

二、同步队列的设计

1.第一种情况:给所有的桶上一个锁(代码如下)

#ifndef SYNCQUEUE4_HPP//等待同步队列
#define SYNCQUEUE4_HPP//这个是给所有桶一个锁,相互联系
#include<iostream>
#include<list>
#include<vector>
#include<mutex>;
#include<condition_variable>
#include<chrono>
using namespace std;
using namespace std::chrono;

template<class Task>
class SyncQueue
{
private:
	std::vector<std::list<Task>>m_queue;//桶
	size_t m_backetSize;//桶的大小(vector)与threadnum相关联
	size_t m_maxSize;//每一个桶的任务上限(list里面的任务)  每一个桶里面的list的数量都不能超过每一个桶的上限
	mutable 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的等待时间

	size_t TotoTaskCount()const //总的任务数
	{
		size_t sum = 0;
		for (auto& tlist : m_queue)
		{
			sum += tlist.size();
		}
		return sum;
	}
	bool IsFull(int index)const
	{
		return m_queue[index].size() >= maxsize;
	}
	bool IsEmpty(int index)const
	{
		return m_queue[index].empty();
	}

	template<class F>
	int Add(F&& f, int index)//添加到生产者  index桶的编号
	{
		std::unique_lock<std::mutex>lock(mux);
		while (!m_stop && IsFull(index))
		{
			//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[index].push_back(std::forward<F>(f));
		cv_x.notify_all();
		return 0;
	}
public:
	SyncQueue(size_t max , size_t bucketsize ) :maxsize(max), m_stop(false) 
	{
		m_queue.resize(bucketsize);//resize会对每个桶里面进行初始化为0
	}
	~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 (TotoTaskCount() != 0)
		{
			//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, int index)//添加任务(生产者) index桶的编号
	{
		return Add(std::forward<Task>(task), index);
	}
	int Put(const Task& task, int index)//添加任务(生产者)
	{
		return Add(task, index);
	}
	int Take(std::list<Task>& val,int index)//消费者-->一次消费所有
	{
		std::unique_lock<std::mutex>lock(mux);
		while (!m_stop && IsEmpty(index))
		{
			//cv_x.wait(lock);
			if (std::cv_status::timeout == cv_x.wait_for(lock, std::chrono::seconds(1)))//看是否超时
			{
				return -1;
			}
		}
		if (m_stop)
		{
			return -2;
		}
		val = std::move(m_queue[index]);
		cv_s.notify_all();
		return 0;
	}
	int Take(Task& val, int index)//消费者-->一次消费一个
	{
		std::unique_lock<std::mutex>lock(mux);
		while (!m_stop && IsEmpty(index))
		{
			//cv_x.wait(lock);
			if (std::cv_status::timeout == cv_x.wait_for(lock, std::chrono::seconds(1)))//看是否超时
			{
				return -1;
			}
		}
		if (m_stop)
		{
			return -2;
		}
		val = m_queue[index].front();
		m_queue[index].pop_front();
		cv_s.notify_all();
		return 0;
	}
	bool Empty()const//判空
	{
		std::unique_lock<std::mutex>lock(mux);
		return TotoTaskCount() == 0;
	}
	bool Full()const //判满
	{
		std::unique_lock<std::mutex>lock(mux);
		return TotoTaskCount() >= maxsize * m_backetSize;
	}
	bool BuckEmpty(int index)const//某个桶判空
	{
		std::unique_lock<std::mutex>lock(mux);
		return m_queue[index].empty();
	}
	bool BuckFull(int index)const //某个桶判满
	{
		std::unique_lock<std::mutex>lock(mux);
		return m_queue[index].size() >= maxsize;
	}
	size_t BuckSize(int index)//某个桶的大小
	{
		std::unique_lock<std::mutex>lock(mux);
		return m_queue[index].size();
	}
	size_t Size()const //队列中元素个数
	{
		std::unique_lock<std::mutex>lock(mux);
		return TotoTaskCount();
	}
	size_t Count()const //队列中元素个数
	{
		return TotoTaskCount();
	}
};



#endif

2.给每一个桶一个锁,相互独立 ---->时间比4更长,但并发度更好

#ifndef SYNCQUEUE5_HPP//等待同步队列
#define SYNCQUEUE5_HPP//这个是给每一个桶一个锁,相互独立 ---->时间比4更长,但并发度更好
#include<iostream>
#include<list>
#include<vector>
#include<mutex>;
#include<condition_variable>
#include<chrono>
#include<memory>
#include<atomic>
using namespace std;
using namespace std::chrono;

template<class Task>
class SyncQueue
{
private:
	std::vector<std::list<Task>>m_queue;//桶
	size_t m_backetSize;//桶的大小(vector)与threadnum相关联
	size_t m_maxSize;//每一个桶的任务上限(list里面的任务)  每一个桶里面的list的数量都不能超过每一个桶的上限

	//mutable std::mutex mux[m_backetSize];
	std::unique_ptr<std::mutex[]>mux;//动态创建锁

	std::condition_variable cv_x;//消费者
	std::condition_variable cv_s;//生产者
	std::condition_variable m_waitStop;//等待停止
	size_t maxsize;//任务上限
	std::atomic_bool m_stop;//false-->没停止(判断同步队列是否停止)
	size_t m_waitTime = 10;//10ms的等待时间

	size_t TotoTaskCount()const //总的任务数
	{
		size_t sum = 0;
		for (auto& tlist : m_queue)
		{
			sum += tlist.size();
		}
		return sum;
	}
	bool IsFull(int index)const
	{
		return m_queue[index].size() >= maxsize;
	}
	bool IsEmpty(int index)const
	{
		return m_queue[index].empty();
	}

	template<class F>
	int Add(F&& f, int index)//添加到生产者  index桶的编号
	{
		std::unique_lock<std::mutex>lock(mux[index]);
		while (!m_stop && IsFull(index))
		{
			//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[index].push_back(std::forward<F>(f));
		cv_x.notify_all();
		return 0;
	}
public:
	SyncQueue(size_t max, size_t bucketsize) :maxsize(max), m_stop(false),m_backetSize(bucketsize)
	{
		m_queue.resize(bucketsize);//resize会对每个桶里面进行初始化为0
		mux = std::make_unique<std::mutex[]>(m_backetSize);//创建一组对象
	}
	~SyncQueue()
	{
	}
	void Stop()//强制停止-->不管同步队列有没有值都进行停止
	{
		{
			//std::unique_lock<std::mutex>lock(mux);
			m_stop = true;
		}
		cv_x.notify_all();
		cv_s.notify_all();
	}
	
	void WaitStop()//等待停止-->等同步队列里的值都进行完了才进行停止
	{
		for (int i = 0; i < m_backetSize; i++)//逐个等待每个桶
		{
			std::unique_lock<std::mutex>lock(mux[i]);
			while (!IsEmpty(i))
			{
				//m_waitStop.wait(lock);
				m_waitStop.wait_for(lock, std::chrono::milliseconds(1));//等待1ms,超过1ms后自己就启动
			}
			cv_x.notify_all();
			cv_s.notify_all();
		}
		m_stop = true;
	}
	int Put(Task&& task, int index)//添加任务(生产者) index桶的编号
	{
		return Add(std::forward<Task>(task), index);
	}
	int Put(const Task& task, int index)//添加任务(生产者)
	{
		return Add(task, index);
	}
	int Take(std::list<Task>& val, int index)//消费者-->一次消费所有
	{
		std::unique_lock<std::mutex>lock(mux[index]);
		while (!m_stop && IsEmpty(index))
		{
			//cv_x.wait(lock);
			if (std::cv_status::timeout == cv_x.wait_for(lock, std::chrono::seconds(1)))//看是否超时
			{
				return -1;
			}
		}
		if (m_stop)
		{
			return -2;
		}
		val = std::move(m_queue[index]);
		cv_s.notify_all();
		return 0;
	}
	int Take(Task& val, int index)//消费者-->一次消费一个
	{
		std::unique_lock<std::mutex>lock(mux[index]);
		while (!m_stop && IsEmpty(index))
		{
			//cv_x.wait(lock);
			if (std::cv_status::timeout == cv_x.wait_for(lock, std::chrono::seconds(1)))//看是否超时
			{
				return -1;
			}
		}
		if (m_stop)
		{
			return -2;
		}
		val = m_queue[index].front();
		m_queue[index].pop_front();
		cv_s.notify_all();
		return 0;
	}
	size_t Count()const //队列中元素个数
	{
		return TotoTaskCount();
	}
	
};



#endif

三.线程池设计

#pragma once
#include"SyncQueue5_WorkStealingPool.hpp"//工作窃取线程池-->更适用于一致性哈希的实现
#include<iostream>
#include<atomic>
#include<functional>
#include<list>
#include<vector>
#include<thread>
#include<chrono>
#include<mutex>
#include<future>
#include<condition_variable>
#include<memory>

class WorkStealingPool//提交任务和获取任务很重要
{
public:
	using Task = std::function<void(void)>;
private:
	SyncQueue<Task>m_taskQueue;
	size_t m_numThreads;//总线程数
	std::vector<std::unique_ptr<std::thread>>m_threadgroup;//管理线程
	std::atomic_bool m_running = false;
	std::once_flag m_flag;

	int threadIndex()const//获得线程的桶号
	{
		static int num = 0;
		return ++num % m_numThreads;//要存放在哪个桶里面,进行随机化
	}
	void Start(int numthreads)//开始线程
	{
		m_running = true;
		for (int i = 0; i < numthreads; i++)//i为线程编号
		{
			m_threadgroup.push_back(std::make_unique<std::thread>(
				&WorkStealingPool::RunningThread, this, i));
		}
	}
	void RunningThread(const int tindex)//线程的入口函数
	{
		while (m_running)
		{
			std::list<Task>tasklist;
			if (m_taskQueue.Take(tasklist, tindex) == 0)
			{
				for (auto& task : tasklist)
				{
					task();
				}
			}
			else
			{
				int i = threadIndex();
				while (i == tindex)
				{
					i = threadIndex();
				}
				if (i != tindex && m_taskQueue.Take(tasklist, i) == 0)
				{
					cout << "偷取任务成功" << endl;
					for (auto& task : tasklist)
					{
						task();
					}
				}
				else
				{
					std::this_thread::yield();//偷取失败让其他线程去运行
				}
			}
		}
	}
	void stopThread()//停止线程
	{
		m_taskQueue.WaitStop();
		m_running = false;
		for (auto& tha : m_threadgroup)
		{
			if (tha && tha->joinable())//joinable看线程是否还活动
			{
				tha->join();//等待线程结束
			}
		}
		m_threadgroup.clear();
	}
public:
	WorkStealingPool(int qsize = 100, int numthreads = 8) :m_taskQueue(qsize, numthreads), m_numThreads(numthreads)//每一个桶里面元素队列大小qsize
	{
		Start(numthreads);
	}
	~WorkStealingPool()
	{
		Stop();
	}
	void Stop()
	{
		std::call_once(m_flag, [this]()->void {stopThread(); });
	}
	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_taskQueue.Put([task] {(*task)(); }, threadIndex()) != 0)//拒绝策略
		{
			cout << "not add........" << endl;
			(*task)();
		}
		return result;
	}
	void excute(const Task& task)
	{
		if (m_taskQueue.Put(task, threadIndex()) != 0)//拒绝策略
		{
			cout << "not add........" << endl;
			task();
		}
	}
	void excute(Task&& task)
	{
		if (m_taskQueue.Put(std::forward<Task>(task), threadIndex()) != 0)
		{
			cout << "not add........" << endl;
			task();
		}
	}
};

四.测试用例

#include"WorkStealingPool.hpp"
#include<thread>
#include<vector>
#include<iostream>
#include<algorithm>
#include<chrono>//时间库
#include<future>//未来体
using namespace std;
using namespace std::chrono;

//------------------------排序测试
std::atomic_int randnum{ 0 };//随机化的此时
std::atomic_int sortnum{ 0 };//排序的次数
void my_print(std::vector<int>& arr)
{
	for (auto& x : arr)
	{
		cout << x << " ";
	}
	cout << "--------------------------" << endl;
}
void my_rand(std::vector<int>& arr)
{
	int n = arr.capacity();
	for (int i = 0; i < n; i++)
	{
		arr.push_back(rand() % 1000000);
	}
	++randnum;
}
void my_sort(std::vector<int>& arr)
{
	std::sort(arr.begin(), arr.end());
	++sortnum;
}
int main()
{
	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();//得到当前时间
	{
		WorkStealingPool mypool(100,16);
		for (int i = 0; i < n; i++)
		{
			arr[i].reserve(elemnum);
			mypool.excute(std::bind(my_rand, std::ref(arr[i])));
		}
		cout << "随机化值完成---------------------" << endl;
	}
	cout << "随机初始化值为:======================" << randnum << endl;
	{
		cout << "排序开始-----------------------------------------------" << endl;
		WorkStealingPool mypool(100,16);
		for (int i = 0; i < n; i++)
		{
			mypool.excute(std::bind(my_sort, std::ref(arr[i])));
		}

		std::this_thread::sleep_for(std::chrono::seconds(15));
		cout << "排序结束-----------------------------------------------" << endl;
	}
	std::int64_t diff = duration_cast<microseconds>(high_resolution_clock::now() - begin).count();//duration_cast<microseconds>强转为微妙,并cout数字化
	cout << " 所用时间差diff为: " << diff << " ms " << endl;

	cout << "初始化次数为------------" << randnum << endl;
	cout << "排序的次数为------------" << sortnum << endl;
	return 0;
}

五、适用场景
1、任务分解型应用:当一个任务需要被分解成多个子任务进行并行处理时,工作窃取式线程池可以自动管理任务的分配和调度,充分利用多核处理器的并行能力,提高任务处理效率。

2、递归型任务:对于递归型的任务,workstealingpool能够适应任务的动态变化,根据需要创建和调度子任务,以实现更高效的递归执行。

3、高吞吐量任务:WorkStealingPool的工作窃取算法可以减小线程之间的竞争,并且能够在任务队列为空时从其他线程窃取任务,从而减少线程的等待时间,提高整体的任务处理吞吐量。适用于高吞吐量的任务场景。

4、CPU密集型任务:对于需要大量的CPU计算而没有I.O阻塞的任务,使用工作窃取式线程池可以更好地充分利用CPU核心,并且可以根据需要增加或减小线程数量,以适应任务的计算量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值