一个线程池的例子

前几天在B站上看到一个线程池的例子,用到了很多不熟的语法,于是把它记录下来,方便后续慢慢研究。

在windows下用vs2017编译测试过,C++14和C++17都能跑通。

 

直接上代码:

ThreadPool.hpp:

#ifndef __MY_THREAD_POOL_INCLUDE__
#define __MY_THREAD_POOL_INCLUDE__

#include <vector>
#include <mutex>
#include <thread>
#include <chrono>
#include <queue>
#include <condition_variable>
#include <future>
#include <memory>

namespace MyCommSpace
{
    class ThreadPool {
    public:
        ThreadPool(int threadNum);

        // 添加任务到队列中. 注意: -> 表示返回值类型后置。
        template<typename F, typename ...Arg>
        auto enques(F&& f, Arg&& ... arg)->std::future<typename std::result_of<F(Arg...)>::type>;

        ~ThreadPool();
    private:
        void worker(); // 线程的执行内容
        bool isstop = false; // true表示线程池停止
        std::condition_variable cv;
        std::mutex mtx;
        std::vector<std::thread> workers; // 线程集合
        std::queue<std::function<void()>> myque; // 任务队列
    };

    ThreadPool::ThreadPool(int threadNum)
    {
        printf("ThreadPool 创建线程池,线程数量为:%d\n", threadNum);
        for (auto i = 0; i < threadNum; i++)
        {
            workers.emplace_back([this]() {
                this->worker();
            });
        }
    }

    ThreadPool::~ThreadPool()
    {
        printf("~ThreadPool 被析构 ++++++ \n");
        {
            std::unique_lock<std::mutex> mmm(mtx);
            isstop = true;
        }

        cv.notify_all(); // 通知所有阻塞中的线程

        for (auto& one : workers) // 等待所有线程执行完毕
        {
            one.join();
        }
        printf("~ThreadPool 被析构 ------ \n");
    }

    /*
        插入任务队列:
            可以理解f是任务执行的函数
            arg是传递进来的参数
            返回值是: std::future<typename std::result_of<F(Arg...)>::type>  可以由f指定。
    */
    template<typename F, typename ...Arg>
    auto ThreadPool::enques(F&& f, Arg&& ... arg) -> std::future<typename std::result_of<F(Arg...)>::type>
    {
        // 获得f执行后的类型
        using functype = typename std::result_of<F(Arg...)>::type;

        // 获得一个智能指针,指向一个呗包装为 functype()的task
        auto task = std::make_shared<std::packaged_task<functype()>>(
            std::bind(std::forward<F>(f), std::forward<Arg>(arg)...)
            );

        // 获得future
        std::future<functype> rsfuture = task->get_future();

        {
            std::lock_guard<std::mutex> lll(mtx);
            if (isstop)
                throw std::runtime_error("线程池已停止......");

            // 添加任务到队列中
            myque.emplace([task]() {
                (*task)();
            });
        }

        // 通知线程去执行任务
        cv.notify_one();
        return rsfuture;
    }

    void ThreadPool::worker()
    {
        while (1)
        {
            std::function<void()> task; // 定义任务

            {
                // 从队列中取得一个任务
                std::unique_lock<std::mutex> lock(mtx);
                cv.wait(lock, [this] {return this->isstop || !this->myque.empty(); });
                // 注意:实际应用时没必要在 myque.empty() 时返回,后续可以继续添加任务。
                if (isstop && myque.empty())
                    return;
                task = std::move(this->myque.front());
                this->myque.pop();
            }

            // 执行任务
            task();
        }
    }

}
#endif // __MY_THREAD_POOL_INCLUDE__

测试:

main.cpp

#include "ThreadPool.hpp"

int GetSum(int a, int b)
{
    std::cout << "GetSum. ThreadID:" << std::this_thread::get_id() << std::endl;
    return a + b;
}

void threadTest()
{
    MyCommSpace::ThreadPool mypool(4);

    for (auto i = 0; i < 30; i++)
    {
#if 1
        auto rsfuture0 = mypool.enques([](int a, int b) ->int {
            std::cout << "ThreadID:" << std::this_thread::get_id() << std::endl;
            return a + b; }, 10 * i, 10 * i);
        std::cout << "thread rs:" << rsfuture0.get() << std::endl;
#else
        // 上述lamda表达式可以写成下面这样,便于理解。
        auto rsfuture0 = mypool.enques(&GetSum, 10 * i, 10 * i);
        std::cout << "thread rs:" << rsfuture0.get() << std::endl;
#endif 
    }
}

int main()
{
    threadTest();
    return 0;
}

 

结果:

可以看到只有4个线程在跑

 

总结:

 

github上面的一个线程池:

https://github.com/mtrebi/thread-pool/blob/master/include/ThreadPool.h

ThreadPool.h:

#pragma once

#include <functional>
#include <future>
#include <mutex>
#include <queue>
#include <thread>
#include <utility>
#include <vector>

#include "SafeQueue.h"

class ThreadPool {
private:
  class ThreadWorker {
  private:
    int m_id;
    ThreadPool * m_pool;
  public:
    ThreadWorker(ThreadPool * pool, const int id)
      : m_pool(pool), m_id(id) {
    }

    void operator()() {
      std::function<void()> func;
      bool dequeued;
      while (!m_pool->m_shutdown) {
        {
          std::unique_lock<std::mutex> lock(m_pool->m_conditional_mutex);
          if (m_pool->m_queue.empty()) {
            m_pool->m_conditional_lock.wait(lock);
          }
          dequeued = m_pool->m_queue.dequeue(func);
        }
        if (dequeued) {
          func();
        }
      }
    }
  };

  bool m_shutdown;
  SafeQueue<std::function<void()>> m_queue;
  std::vector<std::thread> m_threads;
  std::mutex m_conditional_mutex;
  std::condition_variable m_conditional_lock;
public:
  ThreadPool(const int n_threads)
    : m_threads(std::vector<std::thread>(n_threads)), m_shutdown(false) {
  }

  ThreadPool(const ThreadPool &) = delete;
  ThreadPool(ThreadPool &&) = delete;

  ThreadPool & operator=(const ThreadPool &) = delete;
  ThreadPool & operator=(ThreadPool &&) = delete;

  // Inits thread pool
  void init() {
    for (int i = 0; i < m_threads.size(); ++i) {
      m_threads[i] = std::thread(ThreadWorker(this, i));
    }
  }

  // Waits until threads finish their current task and shutdowns the pool
  void shutdown() {
    m_shutdown = true;
    m_conditional_lock.notify_all();
    
    for (int i = 0; i < m_threads.size(); ++i) {
      if(m_threads[i].joinable()) {
        m_threads[i].join();
      }
    }
  }

  // Submit a function to be executed asynchronously by the pool
  template<typename F, typename...Args>
  auto submit(F&& f, Args&&... args) -> std::future<decltype(f(args...))> {
    // Create a function with bounded parameters ready to execute
    std::function<decltype(f(args...))()> func = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
    // Encapsulate it into a shared ptr in order to be able to copy construct / assign 
    auto task_ptr = std::make_shared<std::packaged_task<decltype(f(args...))()>>(func);

    // Wrap packaged task into void function
    std::function<void()> wrapper_func = [task_ptr]() {
      (*task_ptr)(); 
    };

    // Enqueue generic wrapper function
    m_queue.enqueue(wrapper_func);

    // Wake up one thread if its waiting
    m_conditional_lock.notify_one();

    // Return future from promise
    return task_ptr->get_future();
  }
};

 

SafeQueue.h
#pragma once

#include <mutex>
#include <queue>

// Thread safe implementation of a Queue using an std::queue
template <typename T>
class SafeQueue {
private:
  std::queue<T> m_queue;
  std::mutex m_mutex;
public:
  SafeQueue() {

  }

  SafeQueue(SafeQueue& other) {
    //TODO:
  }

  ~SafeQueue() {

  }


  bool empty() {
    std::unique_lock<std::mutex> lock(m_mutex);
    return m_queue.empty();
  }
  
  int size() {
    std::unique_lock<std::mutex> lock(m_mutex);
    return m_queue.size();
  }

  void enqueue(T& t) {
    std::unique_lock<std::mutex> lock(m_mutex);
    m_queue.push(t);
  }
  
  bool dequeue(T& t) {
    std::unique_lock<std::mutex> lock(m_mutex);

    if (m_queue.empty()) {
      return false;
    }
    t = std::move(m_queue.front());
    
    m_queue.pop();
    return true;
  }
};

 

posted @ 2024-09-24 15:34  xcywt  阅读(24)  评论(0编辑  收藏  举报
作者:xcywt
出处:https://www.cnblogs.com/xcywt//
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
如果文中有什么错误,欢迎指出。以免更多的人被误导。