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

来源:互联网 发布:php注册登录系统源码 编辑:程序博客网 时间:2024/04/30 04:33

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

[cpp] view plaincopy
  1. #include <iostream>  
  2. #include <boost/thread/thread.hpp>  
  3. #include <boost/thread/mutex.hpp>  
  4. #include <functional>  
  5. #include <list>  
  6. #include <atomic>  
  7. #include <vector>  
  8. #include <stdlib.h>  
  9. #include <time.h>  
  10. #include <assert.h>  
  11. #include <memory>  
  12. //This class defines a class contains a thread, a task queue  
  13. class cpp11_thread  
  14. {  
  15.     public:  
  16.         cpp11_thread()  
  17.             :m_b_is_finish(false)  
  18.             ,m_pthread(nullptr)  
  19.         {  
  20.   
  21.         }  
  22.         ~cpp11_thread()  
  23.         {  
  24.             if (m_pthread != nullptr)  
  25.                 delete m_pthread;  
  26.             m_list_tasks.clear();  
  27.         }  
  28.     public:  
  29.         //wait until this thread is terminated;  
  30.         void join() {  
  31.             terminate();  
  32.             if (m_pthread!=nullptr)  
  33.                 m_pthread->join();  
  34.             }  
  35.         //wait until this thread has no tasks pending.  
  36.         void wait_for_idle()  
  37.         {  
  38.             while(load())  
  39.                 boost::this_thread::sleep(boost::posix_time::milliseconds(200));  
  40.         }  
  41.         //set the mask to termminiate  
  42.         void terminate() {m_b_is_finish = true; m_cond_incoming_task.notify_one();}  
  43.         //return the current load of this thread  
  44.         size_t load()  
  45.         {  
  46.             size_t sz = 0;  
  47.             m_list_tasks_mutex.lock();  
  48.             sz = m_list_tasks.size();  
  49.             m_list_tasks_mutex.unlock();  
  50.             return sz;  
  51.         }  
  52.         //Append a task to do  
  53.         size_t append(std::function< void (void) > func)  
  54.         {  
  55.             if (m_pthread==nullptr)  
  56.                 m_pthread = new boost::thread(std::bind(&cpp11_thread::run,this));  
  57.             size_t sz = 0;  
  58.             m_list_tasks_mutex.lock();  
  59.             m_list_tasks.push_back(func);  
  60.             sz = m_list_tasks.size();  
  61.             //if there were no tasks before, we should notidy the thread to do next job.  
  62.             if (sz==1)  
  63.                 m_cond_incoming_task.notify_one();  
  64.             m_list_tasks_mutex.unlock();  
  65.             return sz;  
  66.         }  
  67.     protected:  
  68.         std::atomic< bool>                            m_b_is_finish;          //atomic bool var to mark the thread the next loop will be terminated.  
  69.         std::list<std::function< void (void)> >     m_list_tasks;           //The Task List contains function objects  
  70.   
  71.         boost::mutex                                m_list_tasks_mutex;     //The mutex with which we protect task list  
  72.         boost::thread                               *m_pthread;             //inside the thread, a task queue will be maintained.  
  73.         boost::mutex                                m_cond_mutex;           //condition mutex used by m_cond_locker  
  74.         boost::condition_variable                   m_cond_incoming_task;   //condition var with which we notify the thread for incoming tasks  
  75.   
  76.     protected:  
  77.         void run()  
  78.         {  
  79.             // loop wait  
  80.             while (!m_b_is_finish)  
  81.             {  
  82.                 std::function< void (void)> curr_task ;  
  83.                 bool bHasTasks = false;  
  84.                 m_list_tasks_mutex.lock();  
  85.                 if (m_list_tasks.empty()==false)  
  86.                 {  
  87.                     bHasTasks = true;  
  88.                     curr_task = *m_list_tasks.begin();  
  89.                 }  
  90.                 m_list_tasks_mutex.unlock();  
  91.                 //doing task  
  92.                 if (bHasTasks)  
  93.                 {  
  94.                     curr_task();  
  95.                     m_list_tasks_mutex.lock();  
  96.                     m_list_tasks.pop_front();  
  97.                     m_list_tasks_mutex.unlock();  
  98.                 }  
  99.                 if (!load())  
  100.                 {  
  101.                     boost::unique_lock< boost::mutex> m_cond_locker(m_cond_mutex);  
  102.                     boost::system_time const timeout=boost::get_system_time()+ boost::posix_time::milliseconds(5000);  
  103.                     if (m_cond_locker.mutex())  
  104.                         m_cond_incoming_task.timed_wait(m_cond_locker,timeout);//m_cond_incoming_task.wait(m_cond_locker);  
  105.                 }  
  106.             }  
  107.         }  
  108. };  
  109.   
  110. //the thread pool class  
  111. class cpp11_thread_pool  
  112. {  
  113.     public:  
  114.         cpp11_thread_pool(int nThreads)  
  115.             :m_n_threads(nThreads)  
  116.         {  
  117.             assert(nThreads>0 && nThreads<=512);  
  118.             for (int i = 0; i< nThreads ;i++)  
  119.                 m_vec_threads.push_back(std::shared_ptr<cpp11_thread>(new cpp11_thread()));  
  120.         }  
  121.         ~cpp11_thread_pool()  
  122.         {  
  123.   
  124.         }  
  125.     public:  
  126.         //total threads;  
  127.         size_t count(){return m_vec_threads.size();}  
  128.         //wait until all threads is terminated;  
  129.         void join()  
  130.         {  
  131.             for_each(m_vec_threads.begin(),m_vec_threads.end(),[this](std::shared_ptr<cpp11_thread> & item)  
  132.             {  
  133.                 item->terminate();  
  134.                 item->join();  
  135.             });  
  136.         }  
  137.         //wait until this thread has no tasks pending.  
  138.         void wait_for_idle()  
  139.         {  
  140.             int n_tasks = 0;  
  141.             do  
  142.             {  
  143.                 if (n_tasks)  
  144.                     boost::this_thread::sleep(boost::posix_time::milliseconds(200));  
  145.                 n_tasks = 0;  
  146.                 for_each(m_vec_threads.begin(),m_vec_threads.end(),[this,&n_tasks](std::shared_ptr<cpp11_thread> & item)  
  147.                 {  
  148.                     n_tasks += item->load();  
  149.                 });  
  150.             }while (n_tasks);  
  151.   
  152.         }  
  153.         //set the mask to termminiate  
  154.         void terminate()  
  155.         {  
  156.             for_each(m_vec_threads.begin(),m_vec_threads.end(),[this](std::shared_ptr<cpp11_thread> & item)  
  157.             {  
  158.                  item->terminate();  
  159.             });  
  160.         }  
  161.         //return the current load of this thread  
  162.         size_t load(int n)  
  163.         {  
  164.             return (n>=m_vec_threads.size())?0:m_vec_threads[n]->load();  
  165.         }  
  166.         //Append a task to do  
  167.         void append(std::function< void (void) > func)  
  168.         {  
  169.             int nIdx = -1;  
  170.             unsigned int nMinLoad = -1;  
  171.             for (unsigned int i=0;i<m_n_threads;i++)  
  172.             {  
  173.                 if (nMinLoad> m_vec_threads[i]->load())  
  174.                 {  
  175.                     nMinLoad = m_vec_threads[i]->load();  
  176.                     nIdx = i;  
  177.                 }  
  178.             }  
  179.   
  180.             assert(nIdx>=0 && nIdx<m_n_threads);  
  181.             m_vec_threads[nIdx]->append(func);  
  182.         }  
  183.     protected:  
  184.         //NO. threads  
  185.         int m_n_threads;  
  186.         //vector contains all the threads  
  187.         std::vector<std::shared_ptr<cpp11_thread> > m_vec_threads;  
  188. };  
  189.   
  190. //a function which will be executed in sub thread.  
  191. void hello()  
  192. {  
  193.     //sleep for a while  
  194.     boost::this_thread::sleep(boost::posix_time::milliseconds(rand()%900+100));  
  195.     std::cout <<  
  196.         "Hello world, I'm a function runing in a thread!"  
  197.         << std::endl;  
  198. }  
  199.   
  200. //a class has a method, which will be called in a thread different from the main thread.  
  201. class A  
  202. {  
  203.     private:  
  204.         int m_n;  
  205.     public:  
  206.         A(int n)  
  207.         :m_n(n)  
  208.         {}  
  209.         ~A(){}  
  210.     public:  
  211.         void foo (int k)  
  212.         {  
  213.             //sleep for a while  
  214.             boost::this_thread::sleep(boost::posix_time::milliseconds(rand()%900+100));  
  215.             std::cout <<"n*k = "<<k*m_n<<std::endl;  
  216.             m_n++;  
  217.         }  
  218. };  
  219.   
  220. //let's test the thread.  
  221. int main()  
  222. {  
  223.     cpp11_thread_pool thread(2);  
  224.     srand((unsigned int)time(0));  
  225.     A a(1),b(2),c(3);  
  226.     int nsleep = rand()%900+100;  
  227.     //append a simple function task  
  228.     thread.append(&hello);  
  229.     //append lamda  
  230.     thread.append  
  231.     (  
  232.         [&nsleep]()  
  233.         {  
  234.             boost::this_thread::sleep(boost::posix_time::milliseconds(nsleep));  
  235.             std::cout<<"I'm a lamda runing in a thread"<<std::endl;  
  236.         }  
  237.     );  
  238.     //append object method with copy-constructor(value-assignment)  
  239.     thread.append(std::bind(&A::foo,a,10));  
  240.     thread.append(std::bind(&A::foo,b,11));  
  241.     thread.append(std::bind(&A::foo,c,12));  
  242.     thread.append(std::bind(&A::foo,a,100));  
  243.     //append object method with address assignment, will cause the objects' member increase.  
  244.     thread.append(std::bind(&A::foo,&a,10));  
  245.     thread.append(std::bind(&A::foo,&b,11));  
  246.     thread.append(std::bind(&A::foo,&c,12));  
  247.     thread.append(std::bind(&A::foo,&a,100));  
  248.   
  249.     //wait for all tasks done.  
  250.     thread.wait_for_idle();  
  251.     //kill  
  252.     thread.terminate();  
  253.     //wait for killed  
  254.     thread.join();  
  255.   
  256.     //test function  
  257.     std::function < void (void) > func1 = &hello;  
  258.     std::function < void (void) > func2 = &hello;  
  259.     if (func1.target<void (void) >()!=func2.target<void (void)>())  
  260.     return 0;  
  261.     else  
  262.     return 1;  
  263. }  


程序输出:

[plain] view plaincopy
  1. Hello world, I'm a function runing in a thread!  
  2. I'm a lamda runing in a thread  
  3. n*k = 22  
  4. n*k = 10  
  5. n*k = 36  
  6. n*k = 100  
  7. n*k = 22  
  8. n*k = 10  
  9. n*k = 36  
  10. n*k = 200  
  11.   
  12. Process returned 0 (0x0)   execution time : 2.891 s  
  13. 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 很难做到的。
0 0