线程池

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变量。

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

Code


struct thread_pool {
public:
thread_pool(int n = 1) {
pdata = std::make_shared();
for(int i = 0; i lk(pdata->mtx); // for( ; ; )结束后, lk释放, 又再度申请lk锁, 可将lk提到for外
if(!pdata->tasks.empty()) {
auto current = std::move(pdata->tasks.front());
pdata->tasks.pop();
printf("pop out from thread %d\n", i);
lk.unlock();
current();
lk.lock();
} else if(pdata->is_shutdown) {
break ;
} else {
pdata->cv.wait(lk); // for( ; ; )结束后, lk释放, 又再度申请lk锁, 可将lk提到for外
}
}
}
).detach();
}
~thread_pool() {
puts("~thread_pool");
{
std::lock_guard lk(pdata->mtx);
pdata->is_shutdown = true;
}
pdata->cv.notify_all();
}
thread_pool(thread_pool&) = delete; template

void execute(F&& task) {

{

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

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

puts("put in");

}

pdata->cv.notify_one();

} private:

struct data {

std::mutex mtx;

std::condition_variable cv;

bool is_shutdown = false;

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

};

std::shared_ptr pdata;

};

正确代码

struct thread_pool {
public:
thread_pool(int n = 1) {
pdata = std::make_shared<data>();
for(int i = 0; i < n; i++)
std::thread(
[pdata = pdata, i] { // if need i
std::unique_lock<std::mutex> lk(pdata->mtx);
for( ; ; ) {
if(!pdata->tasks.empty()) {
auto current = std::move(pdata->tasks.front());
pdata->tasks.pop();
lk.unlock();
current();
lk.lock();
} else if(pdata->stop) {
break ;
} else {
pdata->cv.wait(lk);
}
}
}
).detach();
}
~thread_pool() {
{
std::lock_guard<std::mutex> lk(pdata->mtx);
pdata->stop = true;
}
pdata->cv.notify_all();
}
thread_pool(thread_pool&&) = default; template<typename F>
void execute(F&& task) {
{
std::lock_guard<std::mutex> lk(pdata->mtx);
pdata->tasks.emplace(std::forward<F>(task));
}
pdata->cv.notify_one();
} private:
struct data {
std::mutex mtx;
std::condition_variable cv;
bool stop = false;
std::queue< std::function<void()> > tasks;
};
std::shared_ptr<data> pdata;
};

mapreduce

每个任务对应一个协程

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

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

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

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

func (mr *Master) forwardRegistrations(ch chan string) {
i := 0
for {
mr.Lock()
if i < len(mr.workers) {
w := mr.workers[i]
go func() { ch <- w }() // send without holding the lock.
i = i + 1
} else {
mr.newCond.Wait()
}
mr.Unlock()
}
} func schedule(jobName string, mapFiles []string, nReduce int, phase jobPhase, registerChan chan string) {
var ntasks int
var n_other int // number of inputs (for reduce) or outputs (for map)
switch phase {
case mapPhase:
ntasks = len(mapFiles)
n_other = nReduce
case reducePhase:
ntasks = nReduce
n_other = len(mapFiles)
} var wg sync.WaitGroup
for i := 0; i < ntasks; i++ {
doTaskArgs := DoTaskArgs{
JobName: jobName,
File: mapFiles[i],
Phase: phase,
TaskNumber: i,
NumOtherPhase: n_other}
wg.Add(1)
go func() {
for {
worker := <-registerChan
if call(worker, "Worker.DoTask", doTaskArgs, nil) {
wg.Done()
registerChan <- worker
break
}
}
}()
}
wg.Wait()
} func Distributed() {
ch := make(chan string)
go mr.forwardRegistrations(ch)
schedule(mr.jobName, mr.files, mr.nReduce, phase, ch)
}

[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. 【转】Redis 基础操作和命令

    笔记 Redis提供了六种基本的数据结构:String,Hash,List,Set,Sorted Set,HyperLogLog. Redis的特点:纯内存操作,单线程工作模型,非阻塞I/O多路复用. ...

  2. C#混音同时录制采集声卡和麦克风话筒

    在项目中,我们可能需要同时录制声卡的声音和麦克风的声音,比如直播间,在线教学.那么如何实现呢?当然是采用SharpCapture!下面开始演示关键代码,您也可以在文末下载全部源码: 设置授权 第一步: ...

  3. Ubuntu 18.04 上使用xrdp远程桌面连接(Windows远程桌面连接)

    Ubuntu18.04设置#安装xrdpsudo apt-get install xrdp #安装vnc4serversudo apt-get install vnc4server tightvncs ...

  4. 【洛谷 P1659】 [国家集训队]拉拉队排练(manacher)

    题目链接 马拉车+简单膜你 #include <cstdio> #include <cstring> #include <algorithm> using name ...

  5. 禁止迅雷极速版被强制升级为迅雷x

    PS:迅雷极速版( ThunderSpeed1.0.34.360 )下载地址: https://pan.baidu.com/s/1wuBOpNbim5jBru03AfSAVg 按照下面的这个路径去找. ...

  6. node+mysql+vue+express项目搭建

    第一步:项目搭建之前首先需要安装node环境和MySQL数据库. 在已经完成上述的条件下开始进行以下操作: npm install @vue/cli -g   (-g 代表全局安装) 初始化项目  v ...

  7. Linux的网络不通流程

    a:xshell连不上的问题第一步:检查网络适配器,是否禁用vmware的虚拟机网卡第二步:检查vmware net8的地址是否为10.0.0.1第三步:检查系统的vmware服务是否启动第四步:检查 ...

  8. hadoop完整集群遇到问题汇总

    1> 设置静态ip: 由于虚拟机在重启之后ip会再次重置,为了后续的麻烦我吗可以设置成静态ip的方式: cd   /etc/sysconfig/network-scripts/ 修改对比如下: ...

  9. springboot学习入门简易版七---springboot2.0使用@Async异步执行方法(17)

    1启动类开启异步调用注解 @SpringBootApplication @EnableAsync //开启异步调用 public class StartApplication { 不开启则异步调用无效 ...

  10. 【JUC】4.Synchronized与ReentrantLock对比

    与synchronized相同,ReentrantLock也是一种互斥锁: synchronized与ReentrantLock的对比: 都是可重入锁 可以再次获取自己的内部锁,即:一个线程获取某对象 ...