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

线程池分类:

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

 

线程池原理:

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

 

构架:

 

 

排队层分析:

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

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

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

 

 

 

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

#include<iostream>
#include<list>
#include<thread>
#include<condition_variable>
#include<mutex>
using namespace std;

template<typename T>
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<T>(x));
	}

	void Take(list<T>& list){
		//获取互斥mutex
		unique_lock<mutex> 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_lock<mutex>locker(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_guard<mutex>locker(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_guard<mutex>locker(m_mutex);
		return m_queue.empty();
	}

	bool Full(){
		lock_guard<mutex>locker(m_mutex);
		return m_queue.size()==m_maxSize;
	}

	size_t Size(){
		lock_guard<mutex>locker(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<<"缓冲区满了,需要等待。。。"<<endl;
		return !full;
	}	

	bool NotEmpty() const{
		bool empty=m_queue.empty();
		if(empty)
			cout<<"缓冲区空了,需要等待...异步层线程ID:"
				<<this_thread::get_id()<<endl;
		return !empty;
	}

	template<typename F>
	void Add(F&&x){
		//获取mutex
		unique_lock<mutex>locker(m_mutex);
		m_notFull.wait(locker, [this]{return m_needStop || NotFull();});
		if(m_needStop) return;
		//队列不满
		m_queue.push_back(forward<F>(x));
		m_notEmpty.notify_one();
	}

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

 

 

线程池的实现——ThreadPool.hpp

#include<thread>
#include<list>
#include<functional>
#include<memory>
#include<atomic>
#include"SyncQueue.hpp"

const int MaxTaskCount=100;
class ThreadPool{
public:
	using Task=function<void()>;
	//创建线程数为当前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>(task));
	}

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

	void Start(int numThreads){
		m_running=true;
		cout<<"创建异步线程的数量:"<<numThreads<<endl;
		//创建线程组
		for(int i=0; i<numThreads; i++){
			m_threadgroup.push_back(make_shared<thread>(&ThreadPool::RunInThread, this));
		}
	}

private:
	void RunInThread(){
		while(m_running){
			//取任务分别执行
			list<Task>list;
			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<shared_ptr<thread>>m_threadgroup;  //处理任务的线程组
	SyncQueue<Task>m_queue;                 //同步队列
	atomic_bool m_running;                  //是否停止的标志
	once_flag m_flag;

};

 

使用示例——main.cpp

#include<iostream>
#include<list>
#include<functional>
#include<memory>
#include<atomic>
#include<thread>
#include<chrono>
#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:"<<thdId<<endl;
			});
		}
	});

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

	this_thread::sleep_for(chrono::seconds(2));
	getchar();
	pool.Stop();
	thd1.join();
	thd2.join();
}


int main(){

	TestThdPool();
	return 0;
}

 

g++编译

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

 

运行结果

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

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

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值