【C/C++】线程池的设计与实现

1.概述

在服务器的操作系统中,线程是非常珍贵的资源。但是线程的创建和销毁是很消耗资源的,甚至在一些条件下,使用线程的效率还不如不用线程。为了解决这个问题,线程池就产生了。线程池就是指预先创造一组线程的机制,这些预先创建的线程在程序启动的时候就已经准备好,等待执行任务。当有新任务需要执行的时候,线程池会从中分配一个空闲的线程来执行任务,而不是每次都要重新创建和销毁线程。
通俗的讲,你开了一个打车公司,普通的创建和销毁线程的方法就相当于:你接到了客户的打车要求,然后你还得要去找出租车,要他给你的客户服务,送完这个客户了这个出租车就走了。下次好几个客户又来找你,你又要去找个出租车。。。(重复上面的操作)。这一通下去不得累死,运营成本也高;线程池的方法就是,我的公司里本来就雇了师傅和出租车,当我的客户找我的时候我可以立马让我的师傅去送他而不用之前的那一堆操作。这样就省了很多事情。
当然,作为老板,你要考虑很多事,比方说打车的客户太多怎么办。。。如果打车的客户太多,师傅忙不过来,你可以找几个临时工来帮忙。公司没那么忙的时候就自动解约。如果实在是过多,临时工也忙不过来怎么办。那可以让客户们排队(虽然这不太好,但线程池是这样的)。

阅读了上面这些,你应该知道本次线程池要达成什么效果了,线程池是什么机制了:

1. 预先创建的线程:在启动的时候就造出来,在不工作的时候挂起而不是销毁,等到池没了才集中销毁。
2. 临时线程:当请求过多的时候就创建临时线程辅助处理任务。
3. 等待队列:任务会在这个队列中等待执行。

构造如下图所示,W4结束了任务,线程池发布了通知,C0弹出:

ThreadObj

既然知道了它的原理,那么构成部分也就知道了。总的来说,一个线程池要包含以下几个部分:

  1. 任务队列:存放等待执行的任务,还必须是线程安全的。
  2. 方法封装器:每一个任务都是一个方法。
  3. 线程池:要求线程安全,不准COPY和赋值。

总体设计如下图所示:

ThreadPoolUML

貌似有点多,不过这没关系,后面是详解


2. 任务队列

任务队列是线程池的重要组成部分,用来存储线程调度任务。如果等待处理的任务过多,任务队列会将其放在这个队列里等待,本次任务队列说到底就是一个双链表,利用Push()(入队)和TryPop()/WaitAndPop()(出队)这两种方法进行操作。
经常被数据结构爱的同学都知道,队列是先进先出的的数据结构,入队的时候从队列的尾部入队,出队的时候从队列的头部出队。本队列的数据结构是一个双链表,那就从链表头出来,链表尾入队。
队列的属性如下代码所示:

ThreadSafeQueue.hpp

template<class Type>
/*队列类*/
class ThreadSafeQueue{
private:
	/*双链表节点类*/
	struct ThreadQueueNode_{
		shared_ptr<Type> data_;					//数据域
		unique_ptr<ThreadQueueNode_> next_;		//后驱指针域
		ThreadQueueNode_* prev_;				//前驱指针域
	};
	
	unique_ptr<ThreadQueueNode_> head_;			//头结点
	mutex headMutex_;							//保护头结点资源的锁
	ThreadQueueNode_* tail_;					//尾结点
	mutex tailMutex_;							//保护尾结点资源的锁
	condition_variable hangUp_;					//条件变量:挂起任务
	atomic_bool boolStop_;						//原子操作:停止

	/*私有方法*/
	
public:
	/*公用方法*/
};


2.1. 任务队列的出队

我们通过一个图来复习一下链表的头部删除方法,在这个链表中,要先定义一个unique_ptr<ThreadQueueNode_> oldHead,用来存储原来的头结点。然后将head_的内容移动到oldHead,再将oldHead的下一个元素的值给到Head,达到删除头节点的目的。
oldHead是一个智能指针,出了生命周期就删除,不用delete

HeadDel

由于链表的头和尾部的元素是共享资源,任何的线程都很可能会修改他们的内容,为了防止内容被其他线程修改,因此需要再操作之前一定要对头/尾结点加锁
本次队列的弹出方式有两种,一种是等待弹出:也就是如果队列内有元素,线程就马上拿出来并执行,没有的话线程就休眠,等待队列有新的元素加入;
第二种就是立即弹出:也就是如果队列内有元素,线程就马上拿出来并执行。没有的话也不会等待,而是会直接返回值/继续后续的操作。代码如下:

ThreadSafeQueue.hpp
等待弹出机制

/*出队操作*/
template<class Type>
unique_ptr<typename ThreadSafeQueue<Type>::ThreadQueueNode_> ThreadSafeQueue<Type>::PopHead_(){
	unique_ptr<ThreadQueueNode_> oldHead = move(head_); 	//将头结点内容移动给oldHead
	head_ = move(oldHead->next_);							//头结点后移
	return oldHead;											//返回oldHead
}

/*等待任务进入队列*/
template<.class Type>
unique_lock<typename ThreadSafeQueue<Type>::ThreadQueueNode_> ThreadSafeNode<Type>::WaitForData__(){
	lock_guard<mutex> headLock(headMutex_);	//先锁住头结点
	/*如果队列为空,会一直等*/
	hangUp.wait(
		headLock,
		[&]() -> bool{
			return head_.get() == GetTail_() || boolStop_.load() == true;
		}
	);
	//等待完毕,交出锁
	return move(headLock);
}

/*移除头结点和获取头结点内容*/
template<class Type>
unique_ptr<typename ThreadSafeQueue<type>::ThreadQueueNode_> ThreadSafeQueue<Type>::WaitPopHead_(){
	unique_lock<mutex> headLock(WaitForData__());	//收到锁
	if(boolStop_.load()){							//如果队列停止
		return nullptr;
	}
	return PopHead_();								//返回头结点
}

/*移除头结点和获取头结点内容(重载)*/
template<class Type>
unique_ptr<typename ThreadSafeQueue<Type>::ThreadQueueNode_> ThreadSafeQueue<Type>::WaitPopHead_(type& value){
	unique_lock<mutex> headLock(WaitForData__());
	if(boolStop_.load()){
		return nullptr;
	}
	value = move(*head_->data_);
	return PopHead_();
}

/*限时出队*/
template<class Type>
bool ThreadSafeQueue<Type>::WaitAndPopTimeOut(int64_t time){
	lock_guard<mutex> headLock(headMutex_);
	//最多等time ms时间,没有的话不再等待,线程将直接返回
	auto result = hangUp_.wait_for(
		headLock,
		std::chrono::milliseconds(time);
		[&]() -> bool{
			return head_.get() == GetTail() && boolStop_.load() == true;
		}
	);
	
	if(boolStop.load()){
		return false;
	}
	return true;
}

/*出队操作*/
template<class Type>
shared_ptr<Type> ThreadSafeQueue<Type>::WaitAndPop(){
    const unique_ptr<ThreadQueueNode_> oldHead = WaitPopHead_();
    if(oldHead == nullptr){
        return nullptr;
    }
    return oldHead->data_;
}

template<class Type>
bool ThreadSafeQueue<Type>::WaitAndPop(Type& value){
    const unique_ptr<ThreadQueueNode_> oldHead = WaitPopHead_(value);
    if(oldHead == nullptr){
        return false;
    }
    return true;
}

下面是非等待弹出机制:非等待弹出机制不会等待队列里塞入任务,如果队列为空会直接返回!

ThreadSafeQueue.hpp
非等待弹出机制

/*非等待弹出*/
template<class Type>
unique_ptr<typename ThreadSafeQueue<Type>::ThreadQueueNode_> ThreadSafeQueue<Type>::TryPopHead_(){
    lock_guard<mutex> headLock(headMutex_);
    if(head_.get() == GetTail_()){
        return unique_ptr<ThreadQueueNode_>();
    }
    return PopHead_();
}

template<class Type>
unique_ptr<typename ThreadSafeQueue<Type>::ThreadQueueNode_> ThreadSafeQueue<Type>::TryPopHead_(Type& value){
    lock_guard<mutex> headLock(headMutex_);
    if(head_.get() == GetTail_()){
        return unique_ptr<ThreadQueueNode_>();
    }
    value = move(*head_->data_);
    return PopHead_();
}

template<class Type>
shared_ptr<Type> ThreadSafeQueue<Type>::TryPop(){
    unique_ptr<ThreadQueueNode_> oldHead = TryPopHead_();
    return oldHead ? oldHead->data_ : shared_ptr<Type>();
}

template<class Type>
bool ThreadSafeQueue<Type>::TryPop(Type& value){
    const unique_ptr<ThreadQueueNode_> oldHead = TryPopHead_(value);
    if(oldHead){
        return true;
    }
    return false;
}


2.2. 任务队列的入队

任务队列的入列没有上面出列的情况那么多,它只需要设定一个newTail再将tail_后移(更新)就可以了,具体过程如下图所示:

PushUML(1)

代码也是比较简单的,具体如下:

ThreadSafeQueue.hpp

template<class Type>
void ThreadSafeQueue<Type>::Push(Type value){
	shared_ptr<Type> newData = make_shared<Type>(move(value)); //创建新值域,将value移动到newData
	unique_ptr<ThreadQueueNode_> newNode(new ThreadQueueNode_);//创建新结点
	
	{
		lock_guard<mutex> tailLock(tailMutex_);
		tail_->data_ = newData;
		ThreadQueueNode_* const newTail = newNode.get();
		newTail->prev_ = tail_;
        tail_->next_ = move(newNode);
        tail_ = newTail;//tail_后移
	}
	
	hangUp_.notify_all(); //通知到所有任务:加了一个任务
}


2.3. 窃取任务

公司里的每个师傅每天都有运送客人的指标,有些师傅由于客人的目的地比较近,完成的工作量小,送的快。有些师傅客人的目的地远,送的慢。这样就会导致一个问题:
有些师傅这一天很快干完了工作指标,在公司摸鱼。有些师傅可能没有完成任务,还要加班,苦不堪言。我这个老板心好啊,知道不能让送的远的师傅太累,所以就让摸鱼的师傅帮那位送的远的师傅完成一些工作量。我们假设每一个师傅都是一个线程, 像这样,一个空闲线程将另一个较为繁忙的线程的任务调到它那完成的机制叫做任务窃取。 任务窃取可以让线程池更加高效的完成任务,充分发挥CPU的并行能力。
那么我们如何窃取任务呢?在队列头部获取大抵是不可以的,这样会造成锁竞争,从尾部获取是最好的方法,我们知道这是一个双链表,head_能往后移动,tail_就不能往前移动了?这当然也是可以的。(虽然它违反队列的机制)
之前我们获取任务都是从头部加锁再获取,只需要关注头部的锁就行,但这次任务窃取是一个获取前端,一个获取后端,因此要两边加上锁。
代码如下:(本次线程池是共享队列,因此这个并没有在DynamicThreadPool里使用)

ThreadSafeQueue.hpp

template<class Type>
bool ThreadSafeQueue<Type>::TrySteal(Type& value){
	/*两边加上锁*/
    unique_lock<mutex> tailLock(tailMutex_, defer_lock);
    unique_lock<mutex> headLock(headMutex_, defer_lock);
    lock(headLock, tailLock);

    if(head_.get() == tail_){
        return false;
    }

	/*获取队列末尾任务*/
    ThreadQueueNode_* prevNode = tail_->prev_;
    value = move(*(prevNode->data_));
    tail_ = prevNode;
    tail_->next_ = nullptr;
    return true;
}



3. 方法封装器

方法的任务,说到底就是由可调用对象组成的,线程执行的也就是这些可调用对象。因此我们需要封装一个类,它可以支持任何的可调用对象(函数,lambda表达式),并支持任何的类型。
为了保证内存安全,我们需要禁止拷贝赋值(但可以保留移动赋值),代码如下:

FunctionWrapper.hpp

#include <iostream>
#include <memory>
#include <functional>

using namespace std;

class FunctionWrapper{
private:
    /* 抽象基类:定义可调用接口 */
    struct ImplementBase_{
        virtual void Call() = 0;        // 纯虚函数:真正执行任务的入口
        virtual ~ImplementBase_(){}     // 虚析构:保证多态删除安全
    };
    
    /* 指向实现对象的智能指针(独占所有权) */
    unique_ptr<ImplementBase_> implement_;
    
    /* 模板实现类:把任意可调用对象包装成 ImplementBase_ */
    template<class Function>
    struct ImplementType_ : public ImplementBase_{
        Function func_;                 // 保存用户传入的可调用对象

        /* 构造函数:移动保存函数对象 */
        ImplementType_(Function&& func) : func_(move(func)){}

        /* 真正执行任务的实现 */
        void Call(){
             func_(); 
        }
    };
    
public:
    /* 万能构造函数:接受任何可调用对象并擦除类型 */
    template<class Function>
    FunctionWrapper(Function&& func) : implement_(new ImplementType_<Function>(move(func))){}

    /* 仿函数调用:触发实际任务 */
    void operator()(){
        implement_->Call();
    }

    /* 默认构造:空包装 */
    FunctionWrapper() = default;

    /* 移动构造:转移所有权 */
    FunctionWrapper(FunctionWrapper&& other) : implement_(move(other.implement_)){}

    /* 移动赋值:转移所有权 */
    FunctionWrapper& operator=(FunctionWrapper&& other){
        implement_ = move(other.implement_);
        return *this;
    }

    /* 禁止拷贝构造 */
    FunctionWrapper(const FunctionWrapper&) = delete;

    /* 禁止拷贝赋值 */
    FunctionWrapper& operator=(const FunctionWrapper&) = delete;
};


4. 线程池

4.1. 线程池的属性

我们在第一章就已经讲到了线程池的机制,就是先创建几个固定的线程,这些线程在程序创建的时候也会被创建出来。但在完成一个任务时,不会立刻销毁,而是先挂起进入空闲状态,直到线程池也停止了才会销毁这些固有线程。如果任务过多,线程池也会自动创建一些临时线程来辅助完成任务,直到线程池没那么繁忙了就会自动销毁。
因此,要组建一个线程池,应该至少有一个固定线程队列,一个临时线程队列还要有一个任务队列。。。具体属性如下。

DynamicThreadPool.hpp

class DynamicThreadPool : public NoneCopy{
private:
	atomic_bool done_;										//任务结束标志							
	condition_variable cv_;									//线程睡眠/唤醒
	unsigned int maxThreads_;								//最大线程数
	JoinThreads joiners_;									//线程join器
	mutex poolMutex_;										//池锁
	ThreadSafeQeueue<FunctionWrapper> workQeueue_;			//任务队列(共享)
	vector<thread> threads_;								//固定线程
	vector<thread> tempThreads_;							//临时线程
	atomic<unsigned int> activeTasks_{0};					//活跃任务计数
	atomic<unsigned int> tempThreadCount_{0};				//临时线程计数
	/*私有方法*/

public:
	/*公有方法*/
};

值得注意的是,为了防止线程池被Copy,这里还设计了一个类NoneCopyDynamicThreadPool继承自它,这样就可以防止线程池被Copy了。
具体代码如下:

DynamicThreadPool.hpp

class NoneCopy{
private:
	NoneCopy(const NoneCopy&) = delete;					//不准拷贝
	NoneCopy& operator=(const NoneCopy&) = delete;		//也不可以拷贝赋值

protected:
	NoneCopy(){};

public:
	~NoneCopy(){}
};	

此外,这里还设计了一个JoinThreads类,它可以在线程池关闭的时候帮助你自动join掉固定线程(但貌似不会管临时线程,不过后面会说)。
具体代码如下:

DynamicThreadPool.hpp

class JoinThreads{
private:
	vector<thread>& threads_;//线程队列

public:
	explicit JoinThreads(vector<thread>& threads) : threads_(threads) {}
	
	~JoinThreads(){
		/*遍历并join掉没有被join的线程*/
		for(unsigned int i = 0; i < threads_.size(); i++){
			if(threads_[i].joinable()){	
				threads_[i].join();
			}
		}
	}
};

JoinThreads在线程池类DynamicThreadPool内是作为一个组合类变量joiners_存在的,将其放在构造方法内,等待其出线程池类作用域后自动触发析构方法,清除固定线程:

 explicit DynamicThreadPool(unsigned int maxThreads = thread::hardware_concurrency() * 4)
        : done_(false), maxThreads_(maxThreads), joiner_(threads_) {...}

4.2.固定线程

固定线程会从任务队列中抓取任务,如果抓到了就执行任务,没抓取到(一般是队列为空)就会等待,等不到就会进入下一个循环。
代码如下:

DynamicThreadPool.hpp

void DynamicThreadPool::WorkerThread(){
	while(!done_){
		FunctionWrapper task;
		/*如果队列不是空的,马上执行任务*/
		if(workQueue_.TryPop(task)){
			task();
			--activeTasks_;
		}
		else{
			/*是空的,先等待300ms,如果没有再进入下一个循环*/
			unique_lock<mutex> lock(poolLock_);
			cv.wait_for(
				lock,
				chrono::milliseconds(300);
				[&]() -> bool{
					return done_ || !workQueue_.Empty();
				}
			);
		}
	}
}

4.3. 临时线程

临时线程往往会在任务繁忙的时候生成(这里设定为超过固定线程三倍),以此来辅助执行任务。在任务没那么繁忙,且自身任务完成后自动销毁。
代码如下:

DynamicThreadPool.hpp

void DynamicThreadPool::TempWorkThread(){
	FunctionWrapper task;
	while(!done_){
		if(workQueue_.TryPop(task){
			task();
			--activeTask_;
			/*检查是不是在繁忙状态,是的话就退出该临时线程*/
			if(activeTask_ <= thread::concurrency() && tempThreadCount_ > 0){
				--tempThreadCount_;
				return;
			}
		}
		else{
			unique_lock<mutex> lock(poolLock_);
			
			/*检查临时线程是不是大于0,是的话就退出*/
			if(workQueue_.Empty() || tempThreadCount_ > 0){
				--tempThreadCount_;
				return;
			}
			/*等待任务*/
			cv.wait_for(
				lock,
				chrono::milliseconds(300),
				[&]() -> bool{
					return done_ || !workQueue_.Empty();
				}
			}
		}
	}

4.3.任务提交

我们在设定好任务后要提交任务到线程池的任务队列中才可以让线程池抓取任务并执行。该线程池使用了Submit来实现这一功能,这是一个future<typename result_of<FunctionType()>::type>类型,其中FunctionType是一个模板类型,它可以把方法返回值包装成异步句柄,并使用get_future()获得返回值。
代码如下:

template<class FunctionType>
future<typename result_of<FunctionType>::type> DynamicThreadPool::Submit(FunctionType func){
	using ResultType = typename result_of<FunctionType()>::type;
    packaged_task<ResultType()> task(move(func));//转化调用对象为异步任务
    auto result = task.get_future();//获得future用于得到结果

    {
        lock_guard<mutex> lock(poolMutex_);//加锁,保护共享任务队列
        workQueue_.Push(FunctionWrapper(move(task)));//塞入任务
        ++activeTasks_;
    }

	/*检查任务是否繁忙*/
    if (activeTasks_ > thread::hardware_concurrency() * 3 && tempThreadCount_ < (maxThreads_ - thread::hardware_concurrency())) {
        lock_guard<mutex> lock(poolMutex_);//加锁,保护临时线程队列
        tempThreads_.emplace_back(&DynamicThreadPool::TempWorkerThread_, this);//加临时线程
        ++tempThreadCount_;
    }

    cv_.notify_one();//通知线程池
    return result;
}

5. 源代码

ThreadSafeQueue.hpp

#include <queue>
#include <memory>
#include <mutex>
#include <atomic>
#include <chrono>
#include <condition_variable>

using namespace std;

template<class Type>

/*线程安全队列*/
class ThreadSafeQueue{

private:

    /*线程安全队列结点*/
    struct ThreadQueueNode_{
        shared_ptr<Type> data_;                 //数据域
        unique_ptr<ThreadQueueNode_> next_;                 //向后指针
        ThreadQueueNode_* prev_;                //向前指针
    };

    mutex headMutex_;                           //头结点锁
    unique_ptr<ThreadQueueNode_> head_;         //头结点
    mutex tailMutex_;                           //尾结点锁
    ThreadQueueNode_* tail_;                    //尾结点
    condition_variable hangUp_;                 //条件变量:挂起
    atomic_bool boolStop_;                      //原子操作:停止

    // 获取尾部结点
    ThreadQueueNode_* GetTail_();

    //出队
    unique_ptr<ThreadQueueNode_> PopHead_();

    //等待执行元素
    unique_lock<ThreadQueueNode_> WaitForData__();

    //等待出队
    unique_ptr<ThreadQueueNode_> WaitPopHead_();

    unique_ptr<ThreadQueueNode_> WaitPopHead_(Type&);

    //尝试出队
    unique_ptr<ThreadQueueNode_> TryPopHead_();

    unique_ptr<ThreadQueueNode_> TryPopHead_(Type&);

public:
    ThreadSafeQueue() : head_(new ThreadQueueNode_), tail_(head_.get()){}

    ~ThreadSafeQueue(){
        boolStop_.store(true);
        hangUp_.notify_all();
    }

    //禁止拷贝和赋值
    ThreadSafeQueue(const ThreadSafeQueue&) = delete;

    ThreadSafeQueue& operator=(const ThreadSafeQueue&) = delete;

    //退出
    void Exit();

    //判断等待是否超时
    bool WaitAndPopTimeOut(Type&);

    //等待并出队
    shared_ptr<Type> WaitAndPop();

    bool WaitAndPop(Type&);

    //尝试出队
    shared_ptr<Type> TryPop();

    bool TryPop(Type&);

    //队列是否空
    bool Empty();

    //入队
    void Push(Type);

    //避免空闲
    bool TrySteal(Type&);
};

template<class Type>
typename ThreadSafeQueue<Type>::ThreadQueueNode_* ThreadSafeQueue<Type>::GetTail_(){
    lock_guard<mutex> tailLock(tailMutex_);//先锁住
    return tail_;//再取值
}

template<class Type>
unique_ptr<typename ThreadSafeQueue<Type>::ThreadQueueNode_> ThreadSafeQueue<Type>::PopHead_(){
    unique_ptr<ThreadQueueNode_> oldHead = move(head_);//先将头结点的内容给予oldHead中
    head_ = move(oldHead -> next_);//再将头结点定义为oldHead后面
    return oldHead;
}

template<class Type>
unique_lock<typename ThreadSafeQueue<Type>::ThreadQueueNode_> ThreadSafeQueue<Type>::WaitForData__(){
    unique_lock<mutex> headLock(headMutex_);//先锁住

    //如果队列是空的就一直等
    hangUp_.wait(
        headLock,
        [&](){
            return head_.get() != GetTail_() || boolStop_.load() == true;
        }
    );

    return move(headLock);//等待完毕后交出锁
}

template<class Type>
unique_ptr<typename ThreadSafeQueue<Type>::ThreadQueueNode_> ThreadSafeQueue<Type>::WaitPopHead_(){
    //先将等待中的数据锁住(接受前面的锁)
    unique_lock<mutex> headLock(WaitForData__());
    if(boolStop_.load()){ //如果任务队列停了
        return nullptr;//返回空指针
    }
    return PopHead_();//返回头结点
}

template<class Type>
unique_ptr<typename ThreadSafeQueue<Type>::ThreadQueueNode_> ThreadSafeQueue<Type>::WaitPopHead_(Type& value){
    unique_lock<mutex> headLock(WaitForData__());//获取WaitForData__()的锁
    if(boolStop_.load()){
        return nullptr;
    }

    value = move(*head_->data_);//将头结点的值move给value
    return PopHead_();
}

template<class Type>
unique_ptr<typename ThreadSafeQueue<Type>::ThreadQueueNode_> ThreadSafeQueue<Type>::TryPopHead_(){
    lock_guard<mutex> headLock(headMutex_);
    if(head_.get() == GetTail_()){				//判断队列是否为空
        return unique_ptr<ThreadQueueNode_>();
    }
    return PopHead_();
}

template<class Type>
unique_ptr<typename ThreadSafeQueue<Type>::ThreadQueueNode_> ThreadSafeQueue<Type>::TryPopHead_(Type& value){
    lock_guard<mutex> headLock(headMutex_);
    if(head_.get() == GetTail_()){
        return unique_ptr<ThreadQueueNode_>();
    }
    value = move(*head_->data_);
    return PopHead_();
}

template<class Type>
void ThreadSafeQueue<Type>::Exit(){
    boolStop_.store(true);			//停止队列
    hangUp_.notify_all();			//通知整个队列
}

template<class Type>
bool ThreadSafeQueue<Type>::WaitAndPopTimeOut(Type& value){
    unique_lock<mutex> headLock(headMutex_);

	//等待300ms,查看有没有其他任务加入,没有的话就执行下一步
    auto result = hangUp_.wait_for(
        headLock,
        chrono::milliseconds(300),
        [&](){
            head_.get() != GetTail_() && boolStop_.load() == true;
        }
    );

    if(boolStop_.load()){
        return false;
    }

    value = move(*head_->data_);
    head_ = move(head_->next_);//指针后移
    return true;
}

template<class Type>
shared_ptr<Type> ThreadSafeQueue<Type>::WaitAndPop(){
    const unique_ptr<ThreadQueueNode_> oldHead = WaitPopHead_();
    if(oldHead == nullptr){
        return nullptr;
    }
    return oldHead->data_;
}

template<class Type>
bool ThreadSafeQueue<Type>::WaitAndPop(Type& value){
    const unique_ptr<ThreadQueueNode_> oldHead = WaitPopHead_(value);
    if(oldHead == nullptr){
        return false;
    }
    return true;
}

template<class Type>
shared_ptr<Type> ThreadSafeQueue<Type>::TryPop(){
    unique_ptr<ThreadQueueNode_> oldHead = TryPopHead_();
    return oldHead ? oldHead->data_ : shared_ptr<Type>();
}

template<class Type>
bool ThreadSafeQueue<Type>::TryPop(Type& value){
    const unique_ptr<ThreadQueueNode_> oldHead = TryPopHead_(value);
    if(oldHead){
        return true;
    }
    return false;
}

template<class Type>
bool ThreadSafeQueue<Type>::Empty(){
    lock_guard<mutex> headLock(headMutex_);
    return head_.get() == GetTail_();
}

template<class Type>
void ThreadSafeQueue<Type>::Push(Type value){
	shared_ptr<Type> newData = make_shared<Type>(move(value)); //创建新值域,将value移动到newData
	unique_ptr<ThreadQueueNode_> newNode(new ThreadQueueNode_);//创建新结点
	
	{
		lock_guard<mutex> tailLock(tailMutex_);
		tail_->data_ = newData;
		ThreadQueueNode_* const newTail = newNode.get();
		newTail->prev_ = tail_;
        tail_->next_ = move(newNode);
        tail_ = newTail;//tail_后移
	}
	
	hangUp_.notify_all(); //通知到所有任务:加了一个任务
}

template<class Type>
bool ThreadSafeQueue<Type>::TrySteal(Type& value){
	/*两边加上锁*/
    unique_lock<mutex> tailLock(tailMutex_, defer_lock);
    unique_lock<mutex> headLock(headMutex_, defer_lock);
    lock(headLock, tailLock);

    if(head_.get() == tail_){
        return false;
    }

	/*获取队列末尾任务*/
    ThreadQueueNode_* prevNode = tail_->prev_;
    value = move(*(prevNode->data_));
    tail_ = prevNode;
    tail_->next_ = nullptr;
    return true;
}

FunctionWrapper.hpp

#include <iostream>
#include <memory>
#include <functional>

using namespace std;

class FunctionWrapper{
private:
    /* 抽象基类:定义可调用接口 */
    struct ImplementBase_{
        virtual void Call() = 0;        // 纯虚函数:真正执行任务的入口
        virtual ~ImplementBase_(){}     // 虚析构:保证多态删除安全
    };
    
    /* 指向实现对象的智能指针(独占所有权) */
    unique_ptr<ImplementBase_> implement_;
    
    /* 模板实现类:把任意可调用对象包装成 ImplementBase_ */
    template<class Function>
    struct ImplementType_ : public ImplementBase_{
        Function func_;                 // 保存用户传入的可调用对象

        /* 构造函数:移动保存函数对象 */
        ImplementType_(Function&& func) : func_(move(func)){}

        /* 真正执行任务的实现 */
        void Call(){
             func_(); 
        }
    };
    
public:
    /* 万能构造函数:接受任何可调用对象并擦除类型 */
    template<class Function>
    FunctionWrapper(Function&& func) : implement_(new ImplementType_<Function>(move(func))){}

    /* 仿函数调用:触发实际任务 */
    void operator()(){
        implement_->Call();
    }

    /* 默认构造:空包装 */
    FunctionWrapper() = default;

    /* 移动构造:转移所有权 */
    FunctionWrapper(FunctionWrapper&& other) : implement_(move(other.implement_)){}

    /* 移动赋值:转移所有权 */
    FunctionWrapper& operator=(FunctionWrapper&& other){
        implement_ = move(other.implement_);
        return *this;
    }

    /* 禁止拷贝构造 */
    FunctionWrapper(const FunctionWrapper&) = delete;

    /* 禁止拷贝赋值 */
    FunctionWrapper& operator=(const FunctionWrapper&) = delete;
};

NoneCopy.hpp

class NoneCopy{
private:
	NoneCopy(const NoneCopy&) = delete;					//不准拷贝
	NoneCopy& operator=(const NoneCopy&) = delete;		//也不可以拷贝赋值

protected:
	NoneCopy(){};

public:
	~NoneCopy(){}
};	

JoinThreads.hpp

class JoinThreads{
private:
	vector<thread>& threads_;//线程队列

public:
	explicit JoinThreads(vector<thread>& threads) : threads_(threads) {}
	
	~JoinThreads(){
		/*遍历并join掉没有被join的线程*/
		for(unsigned int i = 0; i < threads_.size(); i++){
			if(threads_[i].joinable()){	
				threads_[i].join();
			}
		}
	}
};

DynamicThreadPool.hpp

#pragma once
#include <iostream>
#include <atomic>
#include <vector>
#include <thread>
#include <future>
#include <mutex>
#include <condition_variable>
#include "NoneCopy.hpp"
#include "ThreadSafeQueue.hpp"
#include "FunctionWrapper.hpp"
#include "JoinThreads.hpp"

using namespace std;

class DynamicThreadPool : public NoneCopy {
private:
    atomic_bool done_;                          // 线程池停止标志
    ThreadSafeQueue<FunctionWrapper> workQueue_;// 任务队列
    vector<thread> threads_;                    // 固定线程
    vector<thread> tempThreads_;                // 临时线程
    mutex poolMutex_;                           // 保护线程相关变量
    unsigned int maxThreads_;                   // 最大线程上限
    JoinThreads joiner_;                        // 自动join常驻线程
    condition_variable cv_;                     // 线程等待条件
    atomic<unsigned int> activeTasks_{0};       // 当前活跃任务数
    atomic<unsigned int> tempThreadCount_{0};   // 当前临时线程数

    void WorkerThread_();                       // 常驻线程执行函数
    
    void TempWorkerThread_();                   // 临时线程执行函数

public:
    explicit DynamicThreadPool(unsigned int maxThreads = thread::hardware_concurrency() * 4)
        : done_(false), maxThreads_(maxThreads), joiner_(threads_) {
		//固定线程数为CPU内核数量
        const unsigned int initialThreads = thread::hardware_concurrency();
        try {
        	//将其加入固定线程队列
            for (unsigned int i = 0; i < initialThreads; ++i){
                threads_.emplace_back(&DynamicThreadPool::WorkerThread_, this);
            }
        } 
        catch (...) {
            done_ = true;
            workQueue_.Exit();
            throw;
        }
    }

    ~DynamicThreadPool() {
        done_ = true;
        cv_.notify_all();

        // for (auto& thr : threads_){
        //     if (thr.joinable()){
        //          thr.join();
        //     }
        // }
		//这是用来join临时线程的
        for (auto& tempThr : tempThreads_){
            if (tempThr.joinable()){
                tempThr.join();
            }
        }
    }

    template<class FunctionType>
    future<typename result_of<FunctionType()>::type> Submit(FunctionType func);
};


// 常驻线程循环
void DynamicThreadPool::WorkerThread_() {
    while (!done_) {
        FunctionWrapper task;

        if (workQueue_.TryPop(task)) {          // 尝试取任务
            task();                             // 执行任务
            --activeTasks_;                     // 任务计数减1
        } 
        else {
            unique_lock<mutex> lock(poolMutex_);
            cv_.wait_for(
                lock, 
                chrono::milliseconds(100),      // 最多等100ms
                [this](){ 
                    return done_ || !workQueue_.Empty(); 
                }
            );
        }
    }
}

// 临时线程循环
void DynamicThreadPool::TempWorkerThread_() {
    while (!done_) {
        FunctionWrapper task;
        if (workQueue_.TryPop(task)) {          // 尝试取任务
            task();                             // 执行任务
            --activeTasks_;                     // 任务计数减1
            if (activeTasks_ <= thread::hardware_concurrency() * 3) { // 任务少了
                lock_guard<mutex> lock(poolMutex_);
                if (activeTasks_ <= thread::hardware_concurrency() * 3 && tempThreadCount_ > 0) {
                    --tempThreadCount_;         // 减少临时线程计数
                    return;                     // 退出当前线程
                }
            }
        } 
        else {
            unique_lock<mutex> lock(poolMutex_);
            
            if (workQueue_.Empty() && tempThreadCount_ > 0) { // 空队列且有临时线程
                --tempThreadCount_;             // 减少临时线程计数
                return;                         // 退出当前线程
            }

            cv_.wait_for(
                lock, 
                chrono::milliseconds(100),      // 最多等100ms
                [this](){ 
                    return done_ || !workQueue_.Empty();
                 }
            );
        }
    }
}

// 提交任务
template<class FunctionType>
future<typename result_of<FunctionType()>::type> DynamicThreadPool::Submit(FunctionType func) {

    using ResultType = typename result_of<FunctionType()>::type;
    packaged_task<ResultType()> task(move(func));
    auto result = task.get_future();

    {
        lock_guard<mutex> lock(poolMutex_);
        workQueue_.Push(FunctionWrapper(move(task))); // 入队任务
        ++activeTasks_;                               // 任务计数加1
    }

    // 任务数多且未达临时线程上限,则创建临时线程
    if (activeTasks_ > thread::hardware_concurrency() * 3 && tempThreadCount_ < (maxThreads_ - thread::hardware_concurrency())) {
        lock_guard<mutex> lock(poolMutex_);
        tempThreads_.emplace_back(&DynamicThreadPool::TempWorkerThread_, this);
        ++tempThreadCount_;
    }

    cv_.notify_one();         // 唤醒一个等待线程
    return result;
}

posted on 2025-07-26 09:37  楊思瞻  阅读(41)  评论(0)    收藏  举报

导航