线程池

ref: https://github.com/progschj/ThreadPool/blob/master/ThreadPool.h

ref: https://www.jianshu.com/p/eec63026f8d0

思想:

维护一个任务队列,并启动n个线程(消费者)。

注意:

  1. 数据因当通过shared_ptr来管理,否则thread_pool析构后,其它线程访问将产生coredump

    main return 之后,tp被析构,main thread已经结束;但tp内detach的多线程尚未结束,此时detach的线程可能正在执行current(),current结束后,重新进入循环体,std::unique_lockstd::mutex lk(mtx); 但mtx此时已经被析构。

  2. 通过lambda传递任务的参数。

    [=], [&]传递的都是this指针的copy/reference,在thread_pool析构后, this指针将成为野指针。我们因当拷贝shared_ptr。

    [*this]会造成thread_pool的拷贝, 当我们将拷贝函数禁用后,编译错误。

    ref:https://en.cppreference.com/w/cpp/language/lambda

  3. 删除thread_pool的拷贝相关函数。

  4. detach()分离线程。另一做法:用vector保存线程,在~thread_pool()内,执行.join()等待线程break跳出for

  5. stop可设为atomic变量,当执行excute()时,检查stop变量。示例代码中thread_pool析构后,并标记了stop,且工作队列为空时,线程池执行完队列内的job再结束。

    不少人的实现即是将stop作为atomic变量。

  1. // 成员变量:
  2. bool stop
  3. std::queue<std:function<void()>> Q
  4. // 成员变量(互斥管理):
  5. std::mutex mtx
  6. std::condition_variable cv
  7. // 成员函数:
  8. thread_pool(int n = 1)
  9. thread_pool(thread_pool&&) = default;
  10. ~thread_pool()
  11. template<class F> void excute(F&& task)
点击查看错误代码示例

Code


  1. struct thread_pool {
  2. public:
  3. thread_pool(int n = 1) {
  4. pdata = std::make_shared();
  5. for(int i = 0; i lk(pdata->mtx); // for( ; ; )结束后, lk释放, 又再度申请lk锁, 可将lk提到for外
  6. if(!pdata->tasks.empty()) {
  7. auto current = std::move(pdata->tasks.front());
  8. pdata->tasks.pop();
  9. printf("pop out from thread %d\n", i);
  10. lk.unlock();
  11. current();
  12. lk.lock();
  13. } else if(pdata->is_shutdown) {
  14. break ;
  15. } else {
  16. pdata->cv.wait(lk); // for( ; ; )结束后, lk释放, 又再度申请lk锁, 可将lk提到for外
  17. }
  18. }
  19. }
  20. ).detach();
  21. }
  22. ~thread_pool() {
  23. puts("~thread_pool");
  24. {
  25. std::lock_guard lk(pdata->mtx);
  26. pdata->is_shutdown = true;
  27. }
  28. pdata->cv.notify_all();
  29. }
  30. thread_pool(thread_pool&) = delete;
  31. template

  32. void execute(F&& task) {

  33. {

  34. std::lock_guardstd::mutex lk(pdata->mtx);

  35. pdata->tasks.emplace(std::forward(task));

  36. puts("put in");

  37. }

  38. pdata->cv.notify_one();

  39. }
  40. private:

  41. struct data {

  42. std::mutex mtx;

  43. std::condition_variable cv;

  44. bool is_shutdown = false;

  45. std::queue< std::function<void()> > tasks;

  46. };

  47. std::shared_ptr pdata;

  48. };

正确代码

  1. struct thread_pool {
  2. public:
  3. thread_pool(int n = 1) {
  4. pdata = std::make_shared<data>();
  5. for(int i = 0; i < n; i++)
  6. std::thread(
  7. [pdata = pdata, i] { // if need i
  8. std::unique_lock<std::mutex> lk(pdata->mtx);
  9. for( ; ; ) {
  10. if(!pdata->tasks.empty()) {
  11. auto current = std::move(pdata->tasks.front());
  12. pdata->tasks.pop();
  13. lk.unlock();
  14. current();
  15. lk.lock();
  16. } else if(pdata->stop) {
  17. break ;
  18. } else {
  19. pdata->cv.wait(lk);
  20. }
  21. }
  22. }
  23. ).detach();
  24. }
  25. ~thread_pool() {
  26. {
  27. std::lock_guard<std::mutex> lk(pdata->mtx);
  28. pdata->stop = true;
  29. }
  30. pdata->cv.notify_all();
  31. }
  32. thread_pool(thread_pool&&) = default;
  33. template<typename F>
  34. void execute(F&& task) {
  35. {
  36. std::lock_guard<std::mutex> lk(pdata->mtx);
  37. pdata->tasks.emplace(std::forward<F>(task));
  38. }
  39. pdata->cv.notify_one();
  40. }
  41. private:
  42. struct data {
  43. std::mutex mtx;
  44. std::condition_variable cv;
  45. bool stop = false;
  46. std::queue< std::function<void()> > tasks;
  47. };
  48. std::shared_ptr<data> pdata;
  49. };

mapreduce

每个任务对应一个协程

worker可重用,相当于线程池里的一个线程

ntasks为任务数量,即生产者消费者队列进入队列的任务数量

对于每一个任务,启动一个go协程,将该任务分发给某个worker。

如果成功执行,worker可重用,否则,抛弃该worker,采用新的worker。

  1. func (mr *Master) forwardRegistrations(ch chan string) {
  2. i := 0
  3. for {
  4. mr.Lock()
  5. if i < len(mr.workers) {
  6. w := mr.workers[i]
  7. go func() { ch <- w }() // send without holding the lock.
  8. i = i + 1
  9. } else {
  10. mr.newCond.Wait()
  11. }
  12. mr.Unlock()
  13. }
  14. }
  15. func schedule(jobName string, mapFiles []string, nReduce int, phase jobPhase, registerChan chan string) {
  16. var ntasks int
  17. var n_other int // number of inputs (for reduce) or outputs (for map)
  18. switch phase {
  19. case mapPhase:
  20. ntasks = len(mapFiles)
  21. n_other = nReduce
  22. case reducePhase:
  23. ntasks = nReduce
  24. n_other = len(mapFiles)
  25. }
  26. var wg sync.WaitGroup
  27. for i := 0; i < ntasks; i++ {
  28. doTaskArgs := DoTaskArgs{
  29. JobName: jobName,
  30. File: mapFiles[i],
  31. Phase: phase,
  32. TaskNumber: i,
  33. NumOtherPhase: n_other}
  34. wg.Add(1)
  35. go func() {
  36. for {
  37. worker := <-registerChan
  38. if call(worker, "Worker.DoTask", doTaskArgs, nil) {
  39. wg.Done()
  40. registerChan <- worker
  41. break
  42. }
  43. }
  44. }()
  45. }
  46. wg.Wait()
  47. }
  48. func Distributed() {
  49. ch := make(chan string)
  50. go mr.forwardRegistrations(ch)
  51. schedule(mr.jobName, mr.files, mr.nReduce, phase, ch)
  52. }

[C++]线程池 与 [Go] mapreduce的更多相关文章

  1. (转载)JAVA线程池管理

    平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...

  2. JAVA基础拾遗-论线程池的线程粒度划分与深浅放置

    摘要:多线程任务处理对提高性能很有帮助,在Java中提供的线程池也方便了对多线程任务的实现.使用它很简单,而如果进行了不正确的使用,那么代码将陷入一团乱麻.因此如何正确地使用它,如以下分享,这个技能你 ...

  3. 线程池ThreadPoolExecutor源码解读研究(JDK1.8)

    一.什么是线程池 为什么要使用线程池?在多线程并发开发中,线程的数量较多,且每个线程执行一定的时间后就结束了,下一个线程任务到来还需要重新创建线程,这样线程数量特别庞大的时候,频繁的创建线程和销毁线程 ...

  4. 使用Java 线程池的利弊及JDK自带六种创建线程池的方法

    1. 为什么使用线程池 诸如 Web 服务器.数据库服务器.文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务.请求以某种方式到达服务器,这种方式可能是通过网络协 ...

  5. Java线程池管理及分布式Hadoop调度框架搭建

    平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发工程师却在这个上面吃了不少苦头. 怎么做一套简便的线程开发模 ...

  6. JDK线程池的使用

    转载自:https://my.oschina.net/hosee/blog/614319: 摘要: 本系列基于炼数成金课程,为了更好的学习,做了系列的记录. 本文主要介绍: 1. 线程池的基本使用 2 ...

  7. <关于并发框架>Java原生线程池原理及Guava与之的补充

    原创博客,转载请联系博主! 转眼快两个月没有更新自己的博客了. 一来感觉自己要学的东西还是太多,与其花几个小时写下经验分享倒不如多看几点技术书. 二来放眼网上已经有很多成熟的中文文章介绍这些用法,自己 ...

  8. Java并发包线程池之ForkJoinPool即ForkJoin框架(二)

    前言 前面介绍了ForkJoinPool相关的两个类ForkJoinTask.ForkJoinWorkerThread,现在开始了解ForkJoinPool.ForkJoinPool也是实现了Exec ...

  9. java线程池和五种常用线程池的策略使用与解析

    java线程池和五种常用线程池策略使用与解析 一.线程池 关于为什么要使用线程池久不赘述了,首先看一下java中作为线程池Executor底层实现类的ThredPoolExecutor的构造函数 pu ...

随机推荐

  1. [洛谷P5340][TJOI2019]大中锋的游乐场

    题目大意:有$n(n\leqslant10^4)$个点,$m(m\leqslant10^5)$条边的无向图,每个点有一个属性$A/B$,要求$|cnt_A-cnt_B|\leqslant k(k\le ...

  2. 有状态的bean和无状态的bean的区别

    有状态会话bean :每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”:一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束.即每个用户最初都会得到一个初 ...

  3. 阿里云OSS上传文件demo

    1.安装ali-oss npm install ali-oss --save 2.demo 此例中使用到了ElementUI的el-upload组件.因为样式为自定义的 所以没有用element的自动 ...

  4. Java 之 Servlet 体系结构

    Servlet 的体系结构 体系结构示意图: 1.Servlet 接口 如果直接实现这个接口,需要重写里面所有的方法,但是只需要使用 service() 方法,其他的不常用. 2.GenericSer ...

  5. linux运行级

    Linux有0到6个级别,分别对应/etc/rcN.d,N对应7个级别 各运行级详解 0.关机 1.单用户模式,类似于Windows安全模式 2.多用户模式 3.完整的多用户模式.标准运行级 4.不用 ...

  6. IOS SDK详解

    来源:http://blog.csdn.net/column/details/huangwenchen-ios-sdk.html?page=1#42803301 博客专栏>移动开发专栏>I ...

  7. Core Animation笔记(变换)

    1.仿射变换 CGAffineTransformMakeScale : CGAffineTransformMakeTranslation CGAffineTransformMakeRotation(C ...

  8. @PropertySources和@ImportReSources注解

    修改默认加载的配置文件,加载指定的配置文件. @PropertySources 格式:@PropertySources(value={"classpath:xxx.xxx"}) @ ...

  9. Django使用swagger生成接口文档

    参考博客:Django接入Swagger,生成Swagger接口文档-操作解析 Swagger是一个规范和完整的框架,用于生成.描述.调用和可视化RESTful风格的Web服务.总体目标是使客户端和文 ...

  10. 文件系统属性chattr权限

    命令格式 chattr [+-=] [选项] 文件名或目录名 + 增加权限 - 删除权限 = 等于某权限 i 如果对文件赋予i权限,那么不允许对文件进行删除.改名,也不能添加.修改数据:如果对目录添加 ...