这是一个简单的C++11实现的线程池,代码很简单。

原理就是管理一个任务队列和一个工作线程队列。

工作线程不断的从任务队列取任务,然后执行。如果没有任务就等待新任务的到来。添加新任务的时候先添加到任务队列,然后通知任意(条件变量notify_one)一个线程有新的任务来了。

源代码来自https://github.com/progschj/ThreadPool

  1. #ifndef THREAD_POOL_H
  2. #define THREAD_POOL_H
  3. #include <vector>
  4. #include <queue>
  5. #include <memory>
  6. #include <thread>
  7. #include <mutex>
  8. #include <condition_variable>
  9. #include <future>
  10. #include <functional>
  11. #include <stdexcept>
  12. // 线程池类
  13. class ThreadPool {
  14. public:
  15. // 构造函数,传入线程数
  16. ThreadPool(size_t threads);
  17. // 入队任务(传入函数和函数的参数)
  18. template<class F, class... Args>
  19. auto enqueue(F&& f, Args&&... args)
  20. -> std::future<typename std::result_of<F(Args...)>::type>;
  21. // 一个最简单的函数包装模板可以这样写(C++11)适用于任何函数(变参、成员都可以)
  22. // template<class F, class... Args>
  23. // auto enqueue(F&& f, Args&&... args) -> decltype(declval<F>()(declval<Args>()...))
  24. // { return f(args...); }
  25. // C++14更简单
  26. // template<class F, class... Args>
  27. // auto enqueue(F&& f, Args&&... args)
  28. // { return f(args...); }
  29. // 析构
  30. ~ThreadPool();
  31. private:
  32. // need to keep track of threads so we can join them
  33. // 工作线程组
  34. std::vector< std::thread > workers;
  35. // 任务队列
  36. std::queue< std::function<void()> > tasks;
  37. // synchronization 异步
  38. std::mutex queue_mutex; // 队列互斥锁
  39. std::condition_variable condition; // 条件变量
  40. bool stop; // 停止标志
  41. };
  42. // the constructor just launches some amount of workers
  43. // 构造函数仅启动一些工作线程
  44. inline ThreadPool::ThreadPool(size_t threads)
  45. : stop(false)
  46. {
  47. for(size_t i = 0;i<threads;++i)
  48. // 添加线程到工作线程组
  49. workers.emplace_back( // 与push_back类型,但性能更好(与此类似的还有emplace/emlace_front)
  50. [this]
  51. { // 线程内不断的从任务队列取任务执行
  52. for(;;)
  53. {
  54. std::function<void()> task;
  55. {
  56. // 拿锁(独占所有权式)
  57. std::unique_lock<std::mutex> lock(this->queue_mutex);
  58. // 等待条件成立
  59. this->condition.wait(lock,
  60. [this]{ return this->stop || !this->tasks.empty(); });
  61. // 执行条件变量等待的时候,已经拿到了锁(即lock已经拿到锁,没有阻塞)
  62. // 这里将会unlock释放锁,其他线程可以继续拿锁,但此处任然阻塞,等待条件成立
  63. // 一旦收到其他线程notify_*唤醒,则再次lock,然后进行条件判断
  64. // 当[return this->stop || !this->tasks.empty()]的结果为false将阻塞
  65. // 条件为true时候解除阻塞。此时lock依然为锁住状态
  66. // 如果线程池停止或者任务队列为空,结束返回
  67. if(this->stop && this->tasks.empty()){
  68. return;
  69. }
  70. // 取得任务队首任务(注意此处的std::move)
  71. task = std::move(this->tasks.front());
  72. // 从队列移除
  73. this->tasks.pop();
  74. }
  75. // 执行任务
  76. task();
  77. }
  78. }
  79. );
  80. }
  81. // add new work item to the pool
  82. // 添加一个新的工作任务到线程池
  83. template<class F, class... Args>
  84. auto ThreadPool::enqueue(F&& f, Args&&... args)
  85. -> std::future<typename std::result_of<F(Args...)>::type>
  86. {
  87. using return_type = typename std::result_of<F(Args...)>::type;
  88. // 将任务函数和其参数绑定,构建一个packaged_task
  89. auto task = std::make_shared< std::packaged_task<return_type()> >(
  90. std::bind(std::forward<F>(f), std::forward<Args>(args)...)
  91. );
  92. // 获取任务的future
  93. std::future<return_type> res = task->get_future();
  94. {
  95. // 独占拿锁
  96. std::unique_lock<std::mutex> lock(queue_mutex);
  97. // don't allow enqueueing after stopping the pool
  98. // 不允许入队到已经停止的线程池
  99. if(stop){
  100. throw std::runtime_error("enqueue on stopped ThreadPool");
  101. }
  102. // 将任务添加到任务队列
  103. tasks.emplace([task](){ (*task)(); });
  104. }
  105. // 发送通知,唤醒某一个工作线程取执行任务
  106. condition.notify_one();
  107. return res;
  108. }
  109. // the destructor joins all threads
  110. inline ThreadPool::~ThreadPool()
  111. {
  112. {
  113. // 拿锁
  114. std::unique_lock<std::mutex> lock(queue_mutex);
  115. // 停止标志置true
  116. stop = true;
  117. }
  118. // 通知所有工作线程,唤醒后因为stop为true了,所以都会结束
  119. condition.notify_all();
  120. // 等待所有工作线程结束
  121. for(std::thread &worker: workers){
  122. worker.join();
  123. }
  124. }
  125. #endif

C++11的简单线程池代码阅读的更多相关文章

  1. 基于C++11的100行实现简单线程池

    基于C++11的100行实现简单线程池 1 线程池原理 线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小, ...

  2. 基于无锁队列和c++11的高性能线程池

    基于无锁队列和c++11的高性能线程池线程使用c++11库和线程池之间的消息通讯使用一个简单的无锁消息队列适用于linux平台,gcc 4.6以上   标签: <无>   代码片段(6)[ ...

  3. Linux下简单线程池的实现

    大多数的网络服务器,包括Web服务器都具有一个特点,就是单位时间内必须处理数目巨大的连接请求,但是处理时间却是比较短的.在传统的多线程服务器模型中是这样实现的:一旦有个服务请求到达,就创建一个新的服务 ...

  4. 第11章 Windows线程池(1)_传统的Windows线程池

    第11章 Windows线程池 11.1 传统的Windows线程池及API (1)线程池中的几种底层线程 ①可变数量的长任务线程:WT_EXECUTELONGFUNCTION ②Timer线程:调用 ...

  5. Linux简单线程池实现(带源码)

    这里给个线程池的实现代码,里面带有个应用小例子,方便学习使用,代码 GCC 编译可用.参照代码看下面介绍的线程池原理跟容易接受,百度云下载链接: http://pan.baidu.com/s/1i3z ...

  6. [C++]C风格、C++风格和C++11特性的线程池

    线程池概念 假设完成一项任务需要的时间=创建线程时间T1+线程执行任务时间T2+销毁线程时间T3,如果T1+T3的时间远大于T2,通常就可以考虑采取线程池来提高服务器的性能 thread pool就是 ...

  7. 第11章 Windows线程池(3)_私有的线程池

    11.3 私有的线程池 11.3.1 创建和销毁私有的线程池 (1)进程默认线程池 当调用CreateThreadpoolwork.CreateThreadpoolTimer.CreateThread ...

  8. win32线程池代码(WinApi/C++)

    win32线程池代码(WinApi/C++) 健壮, 高效,易用,易于扩, 可用于任何C++编译器 //说明, 这段代码我用了很久, 我删除了自动调整规模的代码(因为他还不成熟)/********** ...

  9. Linux多线程实践(9) --简单线程池的设计与实现

    线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收.所以 ...

随机推荐

  1. RabbitMQ操作

    注意:在rabbitmq中,可以存在多个exchange,exchange只是负责接收消息,然后消息必须发送到给queue中,如果没有queue,消息就丢失了,exchange就相当于交换机,不负责存 ...

  2. spring 拦截器

    1.mvc.xml <!-- 自定义拦截链配置 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping p ...

  3. c# ros

    class MK { Stream connection; TcpClient con; public MK(string ip) { con = new TcpClient(); con.Conne ...

  4. shell及脚本3——正则表达式

    一.正则表达式 1.1. 什么是正则表达式 正则表达式是处理字符串的方法,以行为单位,通过一些特殊符号的辅助,让用户可以轻易进行查找.删除.替换某特定字符串的操作. 1.2. 正则表达式与通配符的区别 ...

  5. poj3417 LCA + 树形dp

    Network Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4478   Accepted: 1292 Descripti ...

  6. Mysql主从架构的复制

    复制类型 (1)基于语句的复制:  在主服务器上执行的SQL语句,在从服务器上执行同样的语句.MySQL默认采用基于语句的复制,效率比较高.  一旦发现没法精确复制时,会自动选着基于行的复制. (2) ...

  7. 【codeforces 442B】 Andrey and Problem

    http://codeforces.com/problemset/problem/442/B (题目链接) 题意 n个人,每个人有p[i]的概率出一道题.问如何选择其中s个人使得这些人正好只出1道题的 ...

  8. 【codeforces 148D】 Bag of mice

    http://codeforces.com/problemset/problem/148/D (题目链接) 题意 包中有w个白鼠,b个黑鼠.公主和龙轮流画老鼠,公主先画,谁先画到白鼠谁就赢.龙每画完一 ...

  9. Sql的decimal、float、double类型的区别

    三者的区别介绍 float:浮点型,含字节数为4,32bit,数值范围为-3.4E38~3.4E38(7个有效位) double:双精度实型,含字节数为8,64bit数值范围-1.7E308~1.7E ...

  10. redis 操作 hash 的测试

    1>hset setname field value hset stuSet name zhangsan:1        2>hget setname field hget stuset ...