开发一个半同步半异步线程池

线程池分类:

  1. 半同步半异步线程池
  2. 领导者追随者线程池——实现见 linux服务器开发 2019.2.7(libevent实现http server,线程池)

 

线程池原理:

  • 预先创建一定数量的线程,当任务请求到来时从线程池中分配一个预先创建的线程的处理任务,线程在处理完任务之后还可以重用,不会被销毁。
  • 避免了大量的线程创建和销毁动作,从而节省了系统资源

 

构架:

开发一个半同步半异步线程池_第1张图片

 

 

排队层分析:

上图中线程池分为三层结构,其中,排队层处于核心地位,因为上侧会将任务加到排队层中,异步服务层同时会去除任务,这里有一个同步的过程。

实现时,排队层就是一个同步队列,允许多个线程同时去添加或者取出任务,并且要保证操作过程是安全的。

线程池有两个活动过程,一个是往同步队列中添加任务,一个是从同步队列中取出任务:

开发一个半同步半异步线程池_第2张图片

 

 

 

同步队列实现代码——SyncQueue.hpp

#include
#include
#include
#include
#include
using namespace std;

template
class SyncQueue{
public:
	SyncQueue(int maxSize):m_maxSize(maxSize), m_needStop(false){}

	void Put(const T&x){
		Add(x);
	}

	void Put(T&&x){
		Add(forward(x));
	}

	void Take(list& list){
		//获取互斥mutex
		unique_lock locker(m_mutex);
		//如果需要停止或者队列不为空,则释放mutex并将线程置于waiting状态
		//wait状态等待其他线程调用notify_one或者notify_all将其唤醒
		m_notEmpty.wait(locker, [this]{return m_needStop || NotEmpty();});

		if(m_needStop) return;
		//取出任务队列中的任务
		list=move(m_queue);
		//唤醒一个处于等待状态的用于添加任务的线程去添加任务
		m_notFull.notify_one();
	}

	void Take(T& t){
		unique_locklocker(m_mutex);
		m_notEmpty.wait(locker, [this]{return m_needStop || NotEmpty();});

		if(m_needStop) return;
		t=m_queue.front();
		m_queue.pop_front();
		m_notFull.notify_one();
	}

	void Stop(){
		{
			lock_guardlocker(m_mutex);
			//m_needStop放在了lock_guard的保护范围之内
			m_needStop=true;
		}
		//放在lock_guard保护范围之外是为了优化
		//notify_all或notify_one会唤醒一个等待的线程,线程被唤醒之后会先获取mutex
		//如果在lock_guard保护内,则唤醒的线程需要lock_guard析构释放mutex才能获取mutex
		//放在lock_guard保护范围之外则无需等待lock_guard释放锁
		m_notFull.notify_all();
		m_notEmpty.notify_all();
	}
	
	bool Empty(){
		lock_guardlocker(m_mutex);
		return m_queue.empty();
	}

	bool Full(){
		lock_guardlocker(m_mutex);
		return m_queue.size()==m_maxSize;
	}

	size_t Size(){
		lock_guardlocker(m_mutex);
		return m_queue.size();
	}

	int Count(){
		return m_queue.size();
	}

private:
	bool NotFull() const{
		bool full=m_queue.size()>=m_maxSize;
		if(full) cout<<"缓冲区满了,需要等待。。。"<
	void Add(F&&x){
		//获取mutex
		unique_locklocker(m_mutex);
		m_notFull.wait(locker, [this]{return m_needStop || NotFull();});
		if(m_needStop) return;
		//队列不满
		m_queue.push_back(forward(x));
		m_notEmpty.notify_one();
	}

private:
	listm_queue;                 //缓冲区
	mutex m_mutex;                  //互斥量和条件变量结合使用
	condition_variable m_notEmpty;  //不为空的条件变量	
	condition_variable m_notFull;   //没有满的条件变量
	int m_maxSize;                  //同步队列最大的size
	bool m_needStop;                //停止标志
};

 

 

线程池的实现——ThreadPool.hpp

#include
#include
#include
#include
#include
#include"SyncQueue.hpp"

const int MaxTaskCount=100;
class ThreadPool{
public:
	using Task=function;
	//创建线程数为当前cpu核数
	ThreadPool(int numThreads=thread::hardware_concurrency()):m_queue(numThreads){}

	~ThreadPool(void){
		//如果没有停止时,则主动停止线程池
		Stop();
	}

	void Stop(){
		//保证多线程情况下只调用一次StopThreadGroup
		call_once(m_flag, [this]{StopThreadGroup();});
	}

	void AddTask(Task&& task){
		m_queue.Put(forward(task));
	}

	void AddTask(const Task&& task){
		m_queue.Put(task);
	}

	void Start(int numThreads){
		m_running=true;
		cout<<"创建异步线程的数量:"<(&ThreadPool::RunInThread, this));
		}
	}

private:
	void RunInThread(){
		while(m_running){
			//取任务分别执行
			listlist;
			m_queue.Take(list);

			for(auto& task : list){
				if(!m_running) return;
				task();
			}
		}
	}

	void StopThreadGroup(){
		m_queue.Stop();
		m_running=false;

		for(auto& thread : m_threadgroup){
			if(thread) thread->join();
		}
		m_threadgroup.clear();
	}

	list>m_threadgroup;  //处理任务的线程组
	SyncQueuem_queue;                 //同步队列
	atomic_bool m_running;                  //是否停止的标志
	once_flag m_flag;

};

 

使用示例——main.cpp

#include
#include
#include
#include
#include
#include
#include
#include"ThreadPool.hpp"

void TestThdPool(){
	ThreadPool pool;
	pool.Start(2);

	thread thd1([&pool]{
		for(int i=0; i<10; i++){
			auto thdId=this_thread::get_id();
			pool.AddTask([thdId]{
				cout<<"同步层线程 1 的线程 ID:"<

 

g++编译

g++ -std=c++11 -pthread -lpthread main.cpp -o ThreadPool

 

运行结果

开发一个半同步半异步线程池_第3张图片

 线程池创建了两个内部的线程,线程ID分别为:。初始时,线程池的同步队列是空的,因此两个线程进入等待状态,知道队列中有数据才开始处理数据。

线程池的上层有两个线程,线程ID分别为:。这两个线程不断的网同步队列中添加数据,这些数据供异步服务层的线程处理。最终结果是,异步层的线程交替处理来自上层的任务,交替打印出上层的线程ID,缓冲区空了就会等待,满了之后也会等待,不会允许无限制的添加任务。

 

 

你可能感兴趣的:(C++基础)