[C++]线程池 与 [Go] mapreduce
线程池
ref: https://github.com/progschj/ThreadPool/blob/master/ThreadPool.h
ref: https://www.jianshu.com/p/eec63026f8d0
思想:
维护一个任务队列,并启动n个线程(消费者)。
注意:
数据因当通过shared_ptr来管理,否则thread_pool析构后,其它线程访问将产生coredump
main return 之后,tp被析构,main thread已经结束;但tp内detach的多线程尚未结束,此时detach的线程可能正在执行current(),current结束后,重新进入循环体,std::unique_lockstd::mutex lk(mtx); 但mtx此时已经被析构。通过lambda传递任务的参数。
[=], [&]传递的都是this指针的copy/reference,在thread_pool析构后, this指针将成为野指针。我们因当拷贝shared_ptr。
[*this]会造成thread_pool的拷贝, 当我们将拷贝函数禁用后,编译错误。
ref:https://en.cppreference.com/w/cpp/language/lambda删除thread_pool的拷贝相关函数。
detach()分离线程。另一做法:用vector保存线程,在~thread_pool()内,执行.join()等待线程break跳出for
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的更多相关文章
- (转载)JAVA线程池管理
平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...
- JAVA基础拾遗-论线程池的线程粒度划分与深浅放置
摘要:多线程任务处理对提高性能很有帮助,在Java中提供的线程池也方便了对多线程任务的实现.使用它很简单,而如果进行了不正确的使用,那么代码将陷入一团乱麻.因此如何正确地使用它,如以下分享,这个技能你 ...
- 线程池ThreadPoolExecutor源码解读研究(JDK1.8)
一.什么是线程池 为什么要使用线程池?在多线程并发开发中,线程的数量较多,且每个线程执行一定的时间后就结束了,下一个线程任务到来还需要重新创建线程,这样线程数量特别庞大的时候,频繁的创建线程和销毁线程 ...
- 使用Java 线程池的利弊及JDK自带六种创建线程池的方法
1. 为什么使用线程池 诸如 Web 服务器.数据库服务器.文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务.请求以某种方式到达服务器,这种方式可能是通过网络协 ...
- Java线程池管理及分布式Hadoop调度框架搭建
平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发工程师却在这个上面吃了不少苦头. 怎么做一套简便的线程开发模 ...
- JDK线程池的使用
转载自:https://my.oschina.net/hosee/blog/614319: 摘要: 本系列基于炼数成金课程,为了更好的学习,做了系列的记录. 本文主要介绍: 1. 线程池的基本使用 2 ...
- <关于并发框架>Java原生线程池原理及Guava与之的补充
原创博客,转载请联系博主! 转眼快两个月没有更新自己的博客了. 一来感觉自己要学的东西还是太多,与其花几个小时写下经验分享倒不如多看几点技术书. 二来放眼网上已经有很多成熟的中文文章介绍这些用法,自己 ...
- Java并发包线程池之ForkJoinPool即ForkJoin框架(二)
前言 前面介绍了ForkJoinPool相关的两个类ForkJoinTask.ForkJoinWorkerThread,现在开始了解ForkJoinPool.ForkJoinPool也是实现了Exec ...
- java线程池和五种常用线程池的策略使用与解析
java线程池和五种常用线程池策略使用与解析 一.线程池 关于为什么要使用线程池久不赘述了,首先看一下java中作为线程池Executor底层实现类的ThredPoolExecutor的构造函数 pu ...
随机推荐
- The Day Two 找到一个具有最大和的连续子数组,返回其最大和
""" 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. 示例: 输入: [-2,1,-3,4,-1,2,1,-5, ...
- idea 中激活 JRebel
JRebel介绍: JRebel是一款JVM插件,它使得Java代码修改后不用重启系统,立即生效.IDEA上原生是不支持热部署的,一般更新了 Java 文件后要手动重启 Tomcat 服务器,修改才能 ...
- IDENTITY、SCOPE_IDENTITY、IDENT_CURRENT的分析
https://www.cnblogs.com/daihuiquan/archive/2013/03/18/2956845.html IDENT_CURRENT.IDENTITY.SCOPE_IDEN ...
- Navicat 连接mysql 报错: Authentication plugin caching_ sha2_password cannot be loaded
出现这个错误的时候, 网上的资料都是修改mysql的登录信息的, ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password ...
- NEST 多IndexType与分页
/// <summary> /// POST /_all/employee/_search?typed_keys=true /// </summary> public void ...
- stage1----航空票务系统需求分析报告
航空票务管理系统需求分析报告 题 目 航空票务管理系统需求分析报告 学 院 信息科学与工程学院 专 业 计算机科学与技术 组 员 ...
- vue使用vuex大体结构
store1.js const state = {} const mutations = {} const actions = {} const getters = {} export default ...
- sql 注入风险
目录 sql 注入风险 什么是sql注入呢? 查看sql注入风险 如何避免 sql 注入风险 pymysql 简单规避注入风险示列 sql 注入风险 什么是sql注入呢? 参考百度 查看sql注入风险 ...
- go语言实现分布式id生成器
本文:https://chai2010.cn/advanced-go-programming-book/ch6-cloud/ch6-01-dist-id.html 分布式id生成器 有时我们需要能够生 ...
- Python学习日记(四) 集合和元祖
元祖的特性:是一个只读列表.可以循环.可以切片,修改数据遵循'儿子'不能改但'孙子'可能可以改. iterable:可迭代对象(元祖.列表.字串.集合) 元祖宣告方式: tu = (1,2,3,['a ...