C++11下的线程池以及灵活的functional + bind + lamda

最近学习了一些C++11的文章,急着动手玩一玩。这次,打算利用boost的thread实现一个线程类,维护一个任务队列,以便可以承载非常灵活的调用。这个线程类可以方便的为后面的线程池打好基础。线程池还是动态均衡,没有什么别的。由于minGW 4.7 对 C++11 thread 不支持,所以采用 boost  代替,linux 下是支持的,只是名字空间不同而已,套路都一样。先上代码:

 

 

#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <functional>
#include <list>
#include <atomic>
#include <vector>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <memory>
//This class defines a class contains a thread, a task queue
class cpp11_thread
{
	public:
		cpp11_thread()
			:m_b_is_finish(false)
			,m_pthread(nullptr)
		{

		}
		~cpp11_thread()
		{
			if (m_pthread != nullptr)
				delete m_pthread;
			m_list_tasks.clear();
		}
	public:
		//wait until this thread is terminated;
		void join() {
			terminate();
			if (m_pthread!=nullptr)
				m_pthread->join();
			}
		//wait until this thread has no tasks pending.
		void wait_for_idle()
		{
			while(load())
				boost::this_thread::sleep(boost::posix_time::milliseconds(200));
		}
		//set the mask to termminiate
		void terminate() {m_b_is_finish = true; m_cond_incoming_task.notify_one();}
		//return the current load of this thread
		size_t load()
		{
			size_t sz = 0;
			m_list_tasks_mutex.lock();
			sz = m_list_tasks.size();
			m_list_tasks_mutex.unlock();
			return sz;
		}
		//Append a task to do
		size_t append(std::function< void (void) > func)
		{
			if (m_pthread==nullptr)
				m_pthread = new boost::thread(std::bind(&cpp11_thread::run,this));
			size_t sz = 0;
			m_list_tasks_mutex.lock();
			m_list_tasks.push_back(func);
			sz = m_list_tasks.size();
			//if there were no tasks before, we should notidy the thread to do next job.
			if (sz==1)
				m_cond_incoming_task.notify_one();
			m_list_tasks_mutex.unlock();
			return sz;
		}
	protected:
		std::atomic< bool>							m_b_is_finish;			//atomic bool var to mark the thread the next loop will be terminated.
		std::list<std::function< void (void)> >		m_list_tasks;			//The Task List contains function objects

		boost::mutex								m_list_tasks_mutex;		//The mutex with which we protect task list
		boost::thread								*m_pthread;				//inside the thread, a task queue will be maintained.
		boost::mutex								m_cond_mutex;			//condition mutex used by m_cond_locker
		boost::condition_variable					m_cond_incoming_task;	//condition var with which we notify the thread for incoming tasks

	protected:
		void run()
		{
			// loop wait
			while (!m_b_is_finish)
			{
				std::function< void (void)> curr_task ;
				bool bHasTasks = false;
				m_list_tasks_mutex.lock();
				if (m_list_tasks.empty()==false)
				{
					bHasTasks = true;
					curr_task = *m_list_tasks.begin();
				}
				m_list_tasks_mutex.unlock();
				//doing task
				if (bHasTasks)
				{
					curr_task();
					m_list_tasks_mutex.lock();
					m_list_tasks.pop_front();
					m_list_tasks_mutex.unlock();
				}
				if (!load())
				{
					boost::unique_lock< boost::mutex> m_cond_locker(m_cond_mutex);
					boost::system_time const timeout=boost::get_system_time()+ boost::posix_time::milliseconds(5000);
					if (m_cond_locker.mutex())
						m_cond_incoming_task.timed_wait(m_cond_locker,timeout);//m_cond_incoming_task.wait(m_cond_locker);
				}
			}
		}
};

//the thread pool class
class cpp11_thread_pool
{
	public:
		cpp11_thread_pool(int nThreads)
			:m_n_threads(nThreads)
		{
			assert(nThreads>0 && nThreads<=512);
			for (int i = 0; i< nThreads ;i++)
				m_vec_threads.push_back(std::shared_ptr<cpp11_thread>(new cpp11_thread()));
		}
		~cpp11_thread_pool()
		{

		}
	public:
		//total threads;
		size_t count(){return m_vec_threads.size();}
		//wait until all threads is terminated;
		void join()
		{
			for_each(m_vec_threads.begin(),m_vec_threads.end(),[this](std::shared_ptr<cpp11_thread> & item)
			{
				item->terminate();
				item->join();
			});
		}
		//wait until this thread has no tasks pending.
		void wait_for_idle()
		{
			int n_tasks = 0;
			do
			{
				if (n_tasks)
					boost::this_thread::sleep(boost::posix_time::milliseconds(200));
				n_tasks = 0;
				for_each(m_vec_threads.begin(),m_vec_threads.end(),[this,&n_tasks](std::shared_ptr<cpp11_thread> & item)
				{
					n_tasks += item->load();
				});
			}while (n_tasks);

		}
		//set the mask to termminiate
		void terminate()
		{
			for_each(m_vec_threads.begin(),m_vec_threads.end(),[this](std::shared_ptr<cpp11_thread> & item)
			{
				 item->terminate();
			});
		}
		//return the current load of this thread
		size_t load(int n)
		{
			return (n>=m_vec_threads.size())?0:m_vec_threads[n]->load();
		}
		//Append a task to do
		void append(std::function< void (void) > func)
		{
			int nIdx = -1;
			unsigned int nMinLoad = -1;
			for (unsigned int i=0;i<m_n_threads;i++)
			{
				if (nMinLoad> m_vec_threads[i]->load())
				{
					nMinLoad = m_vec_threads[i]->load();
					nIdx = i;
				}
			}

			assert(nIdx>=0 && nIdx<m_n_threads);
			m_vec_threads[nIdx]->append(func);
		}
	protected:
		//NO. threads
		int m_n_threads;
		//vector contains all the threads
		std::vector<std::shared_ptr<cpp11_thread> > m_vec_threads;
};

//a function which will be executed in sub thread.
void hello()
{
	//sleep for a while
	boost::this_thread::sleep(boost::posix_time::milliseconds(rand()%900+100));
	std::cout <<
        "Hello world, I'm a function runing in a thread!"
        << std::endl;
}

//a class has a method, which will be called in a thread different from the main thread.
class A
{
	private:
		int m_n;
	public:
		A(int n)
		:m_n(n)
		{}
		~A(){}
	public:
		void foo (int k)
		{
			//sleep for a while
			boost::this_thread::sleep(boost::posix_time::milliseconds(rand()%900+100));
			std::cout <<"n*k = "<<k*m_n<<std::endl;
			m_n++;
		}
};

//let's test the thread.
int main()
{
	cpp11_thread_pool thread(2);
	srand((unsigned int)time(0));
	A a(1),b(2),c(3);
	int nsleep = rand()%900+100;
	//append a simple function task
	thread.append(&hello);
	//append lamda
	thread.append
	(
		[&nsleep]()
		{
			boost::this_thread::sleep(boost::posix_time::milliseconds(nsleep));
			std::cout<<"I'm a lamda runing in a thread"<<std::endl;
		}
	);
	//append object method with copy-constructor(value-assignment)
	thread.append(std::bind(&A::foo,a,10));
	thread.append(std::bind(&A::foo,b,11));
	thread.append(std::bind(&A::foo,c,12));
	thread.append(std::bind(&A::foo,a,100));
	//append object method with address assignment, will cause the objects' member increase.
	thread.append(std::bind(&A::foo,&a,10));
	thread.append(std::bind(&A::foo,&b,11));
	thread.append(std::bind(&A::foo,&c,12));
	thread.append(std::bind(&A::foo,&a,100));

	//wait for all tasks done.
	thread.wait_for_idle();
	//kill
	thread.terminate();
	//wait for killed
	thread.join();

	//test function
	std::function < void (void) > func1 = &hello;
	std::function < void (void) > func2 = &hello;
	if (func1.target<void (void) >()!=func2.target<void (void)>())
    return 0;
    else
    return 1;
}

 

 

 

 

 

程序输出:

Hello world, I'm a function runing in a thread!
I'm a lamda runing in a thread
n*k = 22
n*k = 10
n*k = 36
n*k = 100
n*k = 22
n*k = 10
n*k = 36
n*k = 200

Process returned 0 (0x0)   execution time : 2.891 s
Press any key to continue.

 

下面来看看代码。首先是线程类。

 

第13-99行是线程类。该类实现了一个带任务队列的线程模型。关键部件是62行的std::list<std::function< void (void)> >     m_list_tasks; ,这个fifo 用来承载顺序在子线程运行的任务。任务通过48行的append方法进行追加,64行m_list_tasks_mutex是一个mutex,来保护队列的进出。65行定义的线程对象boost::thread在构造函数中初始化并运行,绑定了本对象的run方法。线程得以运行的关键是run方法,在71-99行定义。该方法首先判断是否有pending的任务,有的话就弹出来执行。如果任务做完了,则使用67行定义的条件变量m_cond_incoming_task 进行wait, 直到新的任务到来,在第56行触发条件,激活队列。

线程类还提供了一些方法,比如load()返回队列的大小,以及terminate终止线程。而后,转到线程池。线程池采用最简单的策略,即直接分配给最空闲的线程。

有了上述封装,main函数就简单多了。可以append几乎所有的东西到线程池,这是以前简单的利用 virtual function 很难做到的。

 

 

 

 

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
C++11引入了许多新特性,其中包括线程池的实现。在引用中的代码中,ZERO_ThreadPool类封装了线程池的功能。线程池的原理是通过维护一个线程队列和一个任务队列来实现的。 在初始化阶段,通过调用init函数来初始化线程池。该函数会创建指定数量的线程,并将其保存在threads_队列中。如果线程池已经被初始化过,则直接返回false。 在启动线程池后,调用start函数。该函数会循环创建指定数量的线程,并将它们放入threads_队列中。每个线程都会调用run函数来执行任务。 当调用exec函数时,会将任务添加到tasks_队列中。其中,std::bind用于绑定一个成员函数和其参数,以及占位符std::placeholders::_1表示传入的参数。 在waitForAllDone函数中,会判断atomic_是否为0且tasks_是否为空。如果是,则表示所有任务已经执行完毕,线程池可以退出。 线程池的stop函数用于停止线程池的运行。它会遍历threads_队列,并调用每个线程的join函数,等待线程执行完毕后再返回。 以上就是C++11新特性线程池的基本原理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [基于C++11新特性手写线程池实现](https://blog.csdn.net/m0_70418130/article/details/126805390)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

丁劲犇

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值