版权声明:转载著名出处 https://blog.csdn.net/gcola007/article/details/78750220

背景

刚粗略看完一遍c++ primer第五版,一直在找一些c++小项目练手,实验楼里面有很多项目,但是会员太贵了,学生党就只能google+github自行搜索完成项目了。注:本文纯提供自己的理解,代码完全照抄,有想法的欢迎评论留言一起讨论。

本文参考:

涉及到的c++11的特性:

  • std::vector
  • std::thread
  • std::mutex
  • std::future
  • std::condition_variable

线程池原理介绍

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。

线程池的组成部分:

  • 线程池管理器(ThreadPoolManager):用于创建并管理线程池
  • 工作线程(WorkThread): 线程池中线程
  • 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。
  • 任务队列:用于存放没有处理的任务。提供一种缓冲机制。

代码

  1. #ifndef ThreadPool_h
  2. #define ThreadPool_h
  3.  
  4. #include <vector>
  5. #include <queue>
  6. #include <thread>
  7. #include <mutex>
  8. #include <condition_variable>
  9. #include <future>
  10. #include <functional>
  11.  
  12. class ThreadPool {
  13. public:
  14. ThreadPool(size_t); //构造函数,size_t n 表示连接数
  15.  
  16. template<class F, class... Args>
  17. auto enqueue(F&& f, Args&&... args) //任务管道函数
  18. -> std::future<typename std::result_of<F(Args...)>::type>; //利用尾置限定符 std future用来获取异步任务的结果
  19.  
  20. ~ThreadPool();
  21. private:
  22. // need to keep track of threads so we can join them
  23. std::vector< std::thread > workers; //追踪线程
  24. // the task queue
  25. std::queue< std::function<void()> > tasks; //任务队列,用于存放没有处理的任务。提供缓冲机制
  26.  
  27. // synchronization 同步?
  28. std::mutex queue_mutex; //互斥锁
  29. std::condition_variable condition; //条件变量?
  30. bool stop;
  31. };
  32.  
  33. // the constructor just launches some amount of workers
  34. inline ThreadPool::ThreadPool(size_t threads): stop(false)
  35. {
  36. for(size_t i = ;i<threads;++i)
  37. workers.emplace_back( //以下为构造一个任务,即构造一个线程
  38. [this]
  39. {
  40. for(;;)
  41. {
  42. std::function<void()> task; //线程中的函数对象
  43. {//大括号作用:临时变量的生存期,即控制lock的时间
  44. std::unique_lock<std::mutex> lock(this->queue_mutex);
  45. this->condition.wait(lock,
  46. [this]{ return this->stop || !this->tasks.empty(); }); //当stop==false&&tasks.empty(),该线程被阻塞 !this->stop&&this->tasks.empty()
  47. if(this->stop && this->tasks.empty())
  48. return;
  49. task = std::move(this->tasks.front());
  50. this->tasks.pop();
  51.  
  52. }
  53.  
  54. task(); //调用函数,运行函数
  55. }
  56. }
  57. );
  58. }
  59.  
  60. // add new work item to the pool
  61. template<class F, class... Args>
  62. auto ThreadPool::enqueue(F&& f, Args&&... args) //&& 引用限定符,参数的右值引用, 此处表示参数传入一个函数
  63. -> std::future<typename std::result_of<F(Args...)>::type>
  64. {
  65. using return_type = typename std::result_of<F(Args...)>::type;
  66. //packaged_task是对任务的一个抽象,我们可以给其传递一个函数来完成其构造。之后将任务投递给任何线程去完成,通过
  67. //packaged_task.get_future()方法获取的future来获取任务完成后的产出值
  68. auto task = std::make_shared<std::packaged_task<return_type()> >( //指向F函数的智能指针
  69. std::bind(std::forward<F>(f), std::forward<Args>(args)...) //传递函数进行构造
  70. );
  71. //future为期望,get_future获取任务完成后的产出值
  72. std::future<return_type> res = task->get_future(); //获取future对象,如果task的状态不为ready,会阻塞当前调用者
  73. {
  74. std::unique_lock<std::mutex> lock(queue_mutex); //保持互斥性,避免多个线程同时运行一个任务
  75.  
  76. // don't allow enqueueing after stopping the pool
  77. if(stop)
  78. throw std::runtime_error("enqueue on stopped ThreadPool");
  79.  
  80. tasks.emplace([task](){ (*task)(); });
    //将task投递给线程去完成,vector尾部压入,std::packaged_task 重载了 operator(),重载后的operator()执行function。因此可以(*task)()可以压入vector<function<void()>>
  81. }
  82. condition.notify_one(); //选择一个wait状态的线程进行唤醒,并使他获得对象上的锁来完成任务(即其他线程无法访问对象)
  83. return res;
  84. }//notify_one不能保证获得锁的线程真正需要锁,并且因此可能产生死锁
  85.  
  86. // the destructor joins all threads
  87. inline ThreadPool::~ThreadPool()
  88. {
  89. {
  90. std::unique_lock<std::mutex> lock(queue_mutex);
  91. stop = true;
  92. }
  93. condition.notify_all(); //通知所有wait状态的线程竞争对象的控制权,唤醒所有线程执行
  94. for(std::thread &worker: workers)
  95. worker.join(); //因为线程都开始竞争了,所以一定会执行完,join可等待线程执行完
  96. }
  97.  
  98. #endif /* ThreadPool_h */

线程池大约100行,下面是运行代码

  1. #include <iostream>
  2. #include <vector>
  3. #include <chrono>
  4.  
  5. #include "ThreadPool.h"
  6.  
  7. int main()
  8. {
  9.  
  10. ThreadPool pool();
  11. std::vector< std::future<int> > results;
  12.  
  13. for(int i = ; i < ; ++i) {
  14. results.emplace_back(
  15. pool.enqueue([i] {
  16. std::cout << "hello " << i << std::endl;
  17. std::this_thread::sleep_for(std::chrono::seconds());
  18. std::cout << "world " << i << std::endl;
  19. return i*i;
  20. })
  21. );
  22. }
  23.  
  24. for(auto && result: results) //通过future.get()获取返回值
  25. std::cout << result.get() << ' ';
  26. std::cout << std::endl;
  27.  
  28. return ;
  29. }

代码剖析

通过新建一个线程池类,以类来管理资源(《c++ effective》资源管理一章有提到)。该类包含3个公有成员函数与5个私有成员:构造函数与析构函数即满足(RAII:Resource Acquisition Is Initialization)。

  • 构造函数接受一个size_t类型的数,表示连接数
  • enqueue表示线程池部分中的任务管道,是一个模板函数
  • workers是一个成员为thread的vector,用来监视线程状态
  • tasks表示线程池部分中的任务队列,提供缓冲机制
  • queue_mutex表示互斥锁
  • condition表示条件变量(互斥锁,条件变量以及stop将在后面通过例子说明)

queue_mutex、condition与stop这三个成员让初次接触多线程的我非常的迷惑,互斥到底是什么意思?为什么需要一个bool量来控制?条件变量condition又是什么?
不懂的可以搜索:多线程的生产者与消费者模型
同时附上condition_variable详解

构造函数ThreadPOOL(size_t):

  • 省略了参数
  • emplace_back相当于push_back但比push_back更为高效
  • wokers压入了一个lambda表达式(即一个匿名函数),表示一个任务(线程),使用for的无限循环,task表示函数对象,线程池中的函数接口在enqueue传入的参数之中,condition.wait(lock,bool),当bool为false的时候,线程将会被堵塞挂起,被堵塞时需要notify_one来唤醒线程才能继续执行

任务队列函数enqueue(F&& f, Args&&… args)

  • 这类多参数模板的格式就是如此
  • -> 尾置限定符,语法就是如此,用来推断auto类型
  • typename与class的区别
  • result_of用来得到返回类型的对象,它有一个成员::type

析构函数~ThreadPool()

  • 通过notify_all可以唤醒线程竞争任务的执行,从而使所有任务不被遗漏

C++笔记--thread pool【转】的更多相关文章

  1. Reporting Service 告警"w WARN: Thread pool pressure. Using current thread for a work item"

    如果Reporting Service偶尔出现不可访问或访问出错情况,这种情况一般没有做监控的话,很难捕捉到.出现这种问题,最好检查Reporting Service的日志文件. 今天早上就遇到这样一 ...

  2. The CLR's Thread Pool

    We were unable to locate this content in zh-cn. Here is the same content in en-us. .NET The CLR's Th ...

  3. MySQL thread pool【转】

    本文来自:http://blog.chinaunix.net/uid-26896862-id-3993773.html 刚刚经历了淘宝的双11,真实感受到了紧张的氛围.尽管DB淡定的度过,但是历程中的 ...

  4. worksteal thread pool

    worksteal的场景 对于一个线程池,每个线程有一个队列,想象这种场景,有的线程队列中有大量的比较耗时的任务堆积,而有的线程队列却是空的,现象就是有的线程处于饥饿状态,而有的线程处于消化不良的状态 ...

  5. Improve Scalability With New Thread Pool APIs

    Pooled Threads Improve Scalability With New Thread Pool APIs Robert Saccone Portions of this article ...

  6. CLR thread pool

    Thread Pooling https://msdn.microsoft.com/en-us/library/windows/desktop/ms686756(v=vs.85).aspx Threa ...

  7. MySQL Thread Pool: Problem Definition

    A new thread pool plugin is now a part of the MySQL Enterprise Edition.In this blog we will cover th ...

  8. Thread Pool Engine, and Work-Stealing scheduling algorithm

    http://pages.videotron.com/aminer/threadpool.htm http://pages.videotron.com/aminer/zip/threadpool.zi ...

  9. DUBBO Thread pool is EXHAUSTED!

    一.问题 在测试环境遇到的异常信息,如下: 16-10-17 00:00:00.033 [New I/O server worker #1-6] WARN  com.alibaba.dubbo.com ...

随机推荐

  1. leetcode — single-number

    /** * Source : https://oj.leetcode.com/problems/single-number/ * * * Given an array of integers, eve ...

  2. REST API设计指导——译自Microsoft REST API Guidelines(二)

    由于文章内容较长,只能拆开发布.翻译的不对之处,请多多指教. 另外:最近团队在做一些技术何架构的研究,视频教程只能争取周末多录制一点,同时预计在下周我们会展开一次直播活动,内容围绕容器技术这块. 所有 ...

  3. SpringBoot集成rabbitmq(一)

    前言 Rabbitmq是一个开源的消息代理软件,是AMQP协议的实现.核心作用就是创建消息队列,异步发送和接收消息.通常用来在高并发中处理削峰填谷.延迟处理.解耦系统之间的强耦合.处理秒杀订单.  入 ...

  4. .Net语言 APP开发平台——Smobiler学习日志:如何在手机上快速实现CandleStickChart控件

    最前面的话:Smobiler是一个在VS环境中使用.Net语言来开发APP的开发平台,也许比Xamarin更方便 一.目标样式 我们要实现上图中的效果,需要如下的操作: 1.从工具栏上的”Smobil ...

  5. .NET WebAPI 利用特性捕捉异常

    声明:本方式及代码只使用与.NET Web API. 先创建类继承ExceptionFilterAttribute类型并复写OnException方法. 代码如下: using System; usi ...

  6. phpstudy等php本地环境运行缓慢的问题解决方法

    我们经常会使用些一键安装包部署本地服务器环境.比如phpstudy.但是会有不少人发现,wordpress等使用数据库的程序打开或者切换页面的速度明显低于静态站点.甚至需要好几秒.这个问题一直困扰了我 ...

  7. 细说addEventListener与事件捕获

    细说addEventListener与事件捕获.事件冒泡(一)addEventListener的基本用法 在复杂的项目开发中,javascript和html的解耦变得至关重要,我们被推荐使用事件动态绑 ...

  8. alibaba fastjson 使用

    // 对象转 json 字符串 User user1 = new User("Marry", 30, new Date()); String str1 = JSON.toJSONS ...

  9. FocusListener焦点监听器

    [FocusListener焦点监听器] public class Demo extends JFrame { public Demo(){ setDefaultCloseOperation(Wind ...

  10. SQLServer之创建隐式事务

    隐式事务创建注意事项 IMPLICIT_TRANSACTIONS为 ON 时,系统处于“隐式”事务模式. 这意味着如果 @@TRANCOUNT = 0,下列任一 Transact-SQL 语句都会开始 ...