• [C++]线程池 与 [Go] mapreduce


    线程池

    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 < n; i++)
          std::thread(
            [=] { // [ = ]: runtime error, value capture this pointer, [ & ]: error, reference capture this pointer
              for( ; ; ) {
                std::unique_lock

    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)
    }
    
  • 相关阅读:
    李洪强经典面试题43
    李洪强经典面试题42
    李洪强经典面试题41-iOS选择题
    HTTP头部解析
    iOS
    内网安全工具之hscan扫描
    跟着百度学PHP[4]OOP面对对象编程-17-多态
    绕过注入学习笔记
    引用/别名
    跟着百度学PHP[4]OOP面对对象编程-16-switch逻辑就语句
  • 原文地址:https://www.cnblogs.com/dirge/p/11596948.html
Copyright © 2020-2023  润新知