美文网首页
C++11 ThreadPool的应用

C++11 ThreadPool的应用

作者: NHFX | 来源:发表于2021-03-14 15:35 被阅读0次

    线程池的应用

    代码结构
    • 任务队列
    • 线程池
    1. 工作线程
    2. 代码如下
    #ifndef WBTHE_THREAD_POOL_H_
    #define WBTHE_THREAD_POOL_H_
    
    #include <cassert>
    #include <condition_variable>
    #include <functional>
    #include <iostream>
    #include <memory>    // unique_ptr
    #include <mutex>
    #include <thread>
    #include <list>
    #include <vector>
    
    
    
    class TaskQueue {
    public:
      TaskQueue() = default;
      virtual ~TaskQueue() = default;
    
      virtual void enqueue(std::function<void()> fn) = 0;
        // 关闭
      virtual void shutdown() = 0;
        // 空闲状态
      virtual void on_idle(){};
    };
    
    class ThreadPool : public TaskQueue {
    public:
      explicit ThreadPool(size_t n) : shutdown_(false) {
        while (n) {
          /**
           *  emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程
           */
          threads_.emplace_back(worker(*this));
          n--;
        }
      }
    
      ThreadPool(const ThreadPool &) = delete;
    
      ~ThreadPool() override = default;
    
     /**
      * 传值方式下的std::function对象保存,使用转移操作std::move()
      */
      void enqueue(std::function<void()> fn) override {
        /**
         * std::unique_lock  锁锁管理模板类,通过对mutex的封装,
         * std::unique_lock对象以独占所有权的方式(unique owership)管理mutex对象的上锁和解锁操作,
         * 即在unique_lock对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而unique_lock的生命周期结束之后,
         * 它所管理的锁对象会被解锁。unique_lock具有lock_guard的所有功能,
         * 而且更为灵活。虽然二者的对象都不能复制,但是unique_lock可以移动(movable),
         * 因此用unique_lock管理互斥对象,可以作为函数的返回值,也可以放到STL的容器中。
         *  https://blog.csdn.net/fengbingchun/article/details/78638138
         */
        std::unique_lock<std::mutex> lock(mutex_);
        jobs_.push_back(std::move(fn));
        cond_.notify_one();
      }
    
      void shutdown() override {
        // Stop all worker threads...
        {
          std::unique_lock<std::mutex> lock(mutex_);
          shutdown_ = true;
        }
    
        cond_.notify_all();
    
        //: - auto 是类型占位符,使用时必须初始化,用作类型的推导,在编译期间替换为变量的实际类型
        //: - auto 与 auto* 没有区别,但是auto声明引用类型时必须接&符号,int i = 10; auto &r = i;
        //: - auto 在同一行声明多个变量时,所有的类型都相同, auto只推导第一个类型。后面的同第一个类型相同
        //: - auto 不能做形参
    
        // 不加“&”的意思是取出array里面的每个对象,
        // 赋给t,t虽然被改变了,但数组里面的对象并没有变;加“&”表示t是数组里面 每个对象的别名
        // Join...
        for (auto &t : threads_) {
          t.join();
        }
      }
    
    private:
      struct worker {
        explicit worker(ThreadPool &pool) : pool_(pool) {}
    
        void operator()() {
          for (;;) {
            std::function<void()> fn;
              //======独占互斥体锁 begin======
            {
              std::unique_lock<std::mutex> lock(pool_.mutex_);
                /**
                 [ ]  不捕获任何外部变量
                 [& ]  以引用方式捕获所有变量,可以修改,但要当心引用无效
                 */
              pool_.cond_.wait(
                  lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });
    
              if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }
    
              fn = pool_.jobs_.front();
              pool_.jobs_.pop_front();
            }
              //======end=====
            /**
             * 静态类型转换 转换为目标类型bool
             */
            assert(true == static_cast<bool>(fn));
            fn();
          }
        }
    
        ThreadPool &pool_;
      };
      friend struct worker;
    
      std::vector<std::thread> threads_;
      std::list< std::function<void()> > jobs_;
    
      bool shutdown_;
    
        /**
         用 std::unique_lock(通过 std::mutex) 来锁住当前线程。当前线程会一直被阻塞,
         直到另外一个线程在相同的 std::condition_variable 对象上调用了 notification 函数来唤醒当前线程。
         */
      std::condition_variable cond_;
      std::mutex mutex_;
    };
    
    #endif   // WBTHE_THREAD_POOL_H_
    
    

    相关文章

      网友评论

          本文标题:C++11 ThreadPool的应用

          本文链接:https://www.haomeiwen.com/subject/hhwqcltx.html