C++11 线程池(threadpool)

1 线程池是什么?

在多任务并发执行的时候往往需要开启很多线程来执行。而一个线程的创建和销毁是需要消耗一部分计算机资源的,而如果一个线程执行任务的资源消耗和创建一个线程的消耗差不多的情况下,那简直太浪费资源了。所以如果有已经创建好的一堆线程等着执行任务,有任务来了,调用一个线程去执行就可以了,不用重新创建一个线程。这样可以省去很多资源消耗。

而线程池就是创建了若干个等待执行任务的线程的容器。线程池就是一个线程的容器,线程池负责容纳,管理,甚至调度线程的执行和任务的分配(其中线程的调度和任务的分配不一定是由线程池来完成这个根据实现的不同有不同的责任分配)。

线程池的基本运行过程是管理一个任务队列,一个线程队列,然后每次去一个任务分配给一个线程去做,一直这样循环。

2 线程池实现原理

前面说过要在线程池中线程是复用的,而任务是不断更换的。但是这一点在语言层面是不支持的,因为一般的thread都是执行一个固定的task函数,当执行完毕后该线程就结束了,然后销毁。所以如何实现task和thread的分配是一个关键的问题。

这里一般有两种解决办法
1、一种是让每一个线程都执行任务调度函数,循环获取一个task,然后执行。
2、每一个形成处于等待任务状态,另有一个主线程来进行任务调度。

其中第一种方法比较简单,实现起来也非常容易,但是该方法有个缺点就是如果线程比较多的情况下会存在对任务队列访问的竞争,从而降低效率。所以这里以第二种方式实现线程池。

大概规划如下:
1、需要对线程进行封装,做好线程自己的同步。
2、需要一个线程容器,比如队列或者列表之类
3、任务队列,如果有优先级要求的化可以加入优先级评价体系。任务队列是一个典型的生产者和消费者模型。
4、需要一个任务调度线程
5、每个工作线程绑定一个任务,如果没有任务的情况下线程处于阻塞状态。当接受到一个任务后线程唤醒然后执行任务。

3 线程池实现

需要声明的一点是该线程池的实现使用了大量的C++11中的内容,编译器用的是vs2017(对C++11支持比较友好)

其中用到的C++11中的内容有:

1、thread
2、mutex
3、condition_variable
4、atomic

5、unique_lock

 4 使用单例,考虑到线程池全局使用一个

使用单例封装线程池

#ifndef THREAD_POOL_H
#define THREAD_POOL_H

#include <vector>
#include <queue>
#include <thread>
#include <atomic>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>

namespace std
{
#define  MAX_THREAD_NUM 256
 
 
	//线程池,可以提交变参函数或拉姆达表达式的匿名函数执行,可以获取执行返回值
	//不支持类成员函数, 支持类静态成员函数或全局函数,Opteron()函数等
	class ThreadPool
	{
	protected:
		/* We need access to the mutex in AddWork, and the variable is only
		* visible in .cxx file, so this method returns it. */
		/* We need access to the mutex in AddWork, and the variable is only
		* visible in .cxx file, so this method returns it. */
		std::mutex &
			GetMutex();

		ThreadPool();
		~ThreadPool()  ;
	private:
	
		using Task = std::function<void()>;
	 
		std::mutex          m_Mutex;
		//空闲线程数量
		std::atomic<int>  idlThrNum;

		/** This is a list of jobs submitted to the thread pool.
		* This is the only place where the jobs are submitted.
		* Filled by AddWork, emptied by ThreadExecute. */
		std::deque<std::function<void()>> m_WorkQueue;

		/** When a thread is idle, it is waiting on m_Condition.
		* AddWork signals it to resume a (random) thread. */
		std::condition_variable m_Condition;

		/** Vector to hold all thread handles.
		* Thread handles are used to delete (join) the threads. */
		std::vector<std::thread> m_Threads;

		/* Has destruction started? */
		std::atomic<bool> m_Stopping = false;
 
		/** The continuously running thread function */
		static void
			ThreadExecute();
		/** To lock on the internal variables */
		//static ThreadPool  m_ThreadPoolInstance;
	public:
		//void ThreadPool::RemoveInstance();
		static ThreadPool*
			GetInstance();
			void AddThreads(int count);

			int GetNumberOfCurrentlyIdleThreads();
		// 提交一个任务
		// 调用.get()获取返回值会等待任务执行完,获取返回值
		// 有两种方法可以实现调用类成员,
		// 一种是使用   bind: .commit(std::bind(&Dog::sayHello, &dog));
		// 一种是用 mem_fn: .commit(std::mem_fn(&Dog::sayHello), &dog)
 
		template <class Function, class... Arguments>
		auto
			AddWork(Function && function, Arguments &&... arguments)
			-> std::future<typename std::result_of<Function(Arguments...)>::type>
		{
			if (m_Stopping.load())    // stop == true ??
				throw std::runtime_error("commit on ThreadPool is stopped.");
			using return_type = typename std::result_of<Function(Arguments...)>::type;

			auto task = std::make_shared<std::packaged_task<return_type()>>(
				std::bind(std::forward<Function>(function), std::forward<Arguments>(arguments)...));

			std::future<return_type> res = task->get_future();
			{
				std::unique_lock<std::mutex> lock(this->GetMutex());
				m_WorkQueue.emplace_back([task]() { (*task)(); });
			}
			m_Condition.notify_one();
			return res;
		}
		//空闲线程数量
		int idlCount() { return idlThrNum; }

	};

}

#endif

CPP

// ThreadPool.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "threadpool.h"

#define DEFAULT_THREAD_COUNT 4
namespace std {
	ThreadPool *
		ThreadPool::GetInstance()
	{
		static ThreadPool g_ThreadPoolInstance;
		return   &g_ThreadPoolInstance;
	}

	ThreadPool::ThreadPool()
	{
		for (unsigned int i = 0; i < DEFAULT_THREAD_COUNT; ++i)
		{
			m_Threads.emplace_back(&ThreadExecute);
		}
	}

	void ThreadPool::AddThreads(int count)
	{
		std::unique_lock<std::mutex> mutexHolder(m_Mutex);
		m_Threads.reserve(m_Threads.size() + count);
		for (unsigned int i = 0; i < count; ++i)
		{
			m_Threads.emplace_back(&ThreadExecute);
		}
	}

	std::mutex &
		ThreadPool::GetMutex()
	{
		return m_Mutex;
	}

	void
		ThreadPool::ThreadExecute()
	{
		// plain pointer does not increase reference count
		ThreadPool * threadPool = GetInstance();

		while (!threadPool->m_Stopping.load())
		{
			std::function<void()> task;

			{
				std::unique_lock<std::mutex> mutexHolder(threadPool->m_Mutex);

				threadPool->m_Condition.wait(mutexHolder,
					[threadPool] { return threadPool->m_Stopping.load() || !threadPool->m_WorkQueue.empty(); });
				if (threadPool->m_Stopping.load()&& threadPool->m_WorkQueue.empty())
				{
					printf("qq---------------------------ThreadExecute ----------------- exit\n");
					return;
				}
				task = std::move(threadPool->m_WorkQueue.front());
				threadPool->m_WorkQueue.pop_front();
			}

			task(); // execute the task
		}
		printf("ww-------------------------------ThreadExecute ----------------- exit\n");

	}

	int
		ThreadPool::GetNumberOfCurrentlyIdleThreads()  
	{
		std::unique_lock<std::mutex> mutexHolder(m_Mutex);
	 
		return int(m_Threads.size()) - int(m_WorkQueue.size()); // lousy approximation
	}
	ThreadPool:: ~ThreadPool()
	{
		m_Stopping.store(true);
		m_Condition.notify_all(); // 唤醒所有线程执行
		for (auto & m_Thread : m_Threads)
		{
			if (m_Thread.joinable())
				m_Thread.join();
		}
		printf("~ThreadPool()---------------------ThreadExecute ----------------- exit\n");
	}
 
}

使用

// ThreadPool.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "threadpool.h"


#include "threadpool.h"
#include <iostream>
using namespace std;
void fun1(int slp)
{
	printf("  hello, fun1 !  %d\n", std::this_thread::get_id());
	if (slp>0) {
		printf(" ======= fun1 sleep %d  =========  %d\n", slp, std::this_thread::get_id());
		std::this_thread::sleep_for(std::chrono::milliseconds(slp));
	}
}

struct gfun {
	int operator()(int n) {
		printf("%d  hello, gfun !  %d\n", n, std::this_thread::get_id());
		return 42;
	}
};

class A {
public:
	static int Afun(int n = 0) {   //函数必须是 static 的才能直接使用线程池
		std::cout << n << "  hello, Afun !  " << std::this_thread::get_id() << std::endl;
		return n;
	}

	static std::string Bfun(int n, std::string str, char c) {
		std::cout << n << "  hello, Bfun !  " << str.c_str() << "  " << (int)c << "  " << std::this_thread::get_id() << std::endl;
		return str;
	}
};

int main()
try {
	ThreadPool * executor = ThreadPool::GetInstance();
	executor->AddThreads(50);
	A a;
	std::future<void> ff = executor->AddWork(fun1, 0);
	std::future<int> fg = executor->AddWork(gfun{}, 0);
	std::future<int> gg = executor->AddWork(a.Afun, 9999); //IDE提示错误,但可以编译运行
	std::future<std::string> gh = executor->AddWork(A::Bfun, 9998, "mult args", 123);
	std::future<std::string> fh = executor->AddWork([]()->std::string { std::cout << "hello, fh !  " << std::this_thread::get_id() << std::endl; return "hello,fh ret !"; });

	std::cout << " =======  sleep ========= " << std::this_thread::get_id() << std::endl;
	std::this_thread::sleep_for(std::chrono::microseconds(90));

	for (int i = 0; i < 50; i++) {
		executor->AddWork(fun1, i * 100);
	}
	std::cout << " =======  commit all ========= " << std::this_thread::get_id() << " idlsize=" << executor->GetNumberOfCurrentlyIdleThreads() << std::endl;

	std::cout << " =======  sleep ========= " << std::this_thread::get_id() << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(3));

	ff.get(); //调用.get()获取返回值会等待线程执行完,获取返回值
	std::cout << fg.get() << "  " << fh.get().c_str() << "  " << std::this_thread::get_id() << std::endl;

	std::cout << " =======  sleep ========= " << std::this_thread::get_id() << std::endl;
	std::this_thread::sleep_for(std::chrono::seconds(3));

	std::cout << " =======  fun1,55 ========= " << std::this_thread::get_id() << std::endl;
	executor->AddWork(fun1, 55).get();    //调用.get()获取返回值会等待线程执行完

	std::cout << "end... " << std::this_thread::get_id() << std::endl;


	//std::threadpool pool(4);
	//std::vector< std::future<int> > results;

	//for (int i = 0; i < 8; ++i) {
	//	results.emplace_back(
	//		pool.commit([i] {
	//		std::cout << "hello " << i << std::endl;
	//		std::this_thread::sleep_for(std::chrono::seconds(1));
	//		std::cout << "world " << i << std::endl;
	//		return i*i;
	//	})
	//	);
	//}
	//std::cout << " =======  commit all2 ========= " << std::this_thread::get_id() << std::endl;

	//for (auto && result : results)
	//	std::cout << result.get() << ' ';
	//std::cout << std::endl;
	//executor->RemoveInstance();
	return 0;
}
catch (std::exception& e) {
	std::cout << "some unhappy happened...  " << std::this_thread::get_id() << e.what() << std::endl;
}

Demo 下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

恋恋西风

up up up

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值