C++ 公共组件-线程池实现

我只是一个虾纸丫 提交于 2020-03-11 13:02:48

C++ 公共组件-线程池实现

线程池模型介绍

在处理大量并发任务的时候,如果按照传统的方式,一个请求一个线程来处理请求任务,大量的线程创建和销毁将消耗过多的系统资源,还增加了线程上下文切换的开销,而通过线程池技术就可以很好地解决这些问题。线程池技术通过在系统中预先创建一定数量的线程,当任务请求到来时从线程池中分配一个预先创建的线程去处理任务,线程在处理完任务之后还可以重用,不会销毁,而是等待下次任务的到来。这样,通过线程池能避免大量的线程创建和销毁动作,从而节省系统资源,这样做的一个好处是,对于多核处理器,由于线程会被分配到多个CPU,会提高并行处理的效率。另一个好处是每个线程独立阻塞,可以防止主线程被阻塞而使主流程被阻塞,导致其他的请求得不到响应的问题。

线程池分为半同步半异步线程池和领导者追随者线程池,本章将主要介绍半同步半异步线程池,这种线程池在实现上更简单,使用得比较多,也比较方便。半同步半异步线程池分成三层。如下图显示
在这里插入图片描述
第一层是同步服务层,它处理来自上层的任务请求,上层的请求可能是并发的,这些请求不是马上就会被处理,而是将这些任务放到一个同步排队层中,等待处理。第二层是同步排队层,来自上层的任务请求都会加到排队层中等待处理。第三层是异步服务层,这一层中会有多个线程同时处理排队层中的任务,异步服务层从同步排队层中取出任务并行的处理。

这种三层的结构可以最大程度处理上层的并发请求。对于上层来说只要将任务丢到同步队列中就行了,至于谁去处理,什么时候处理都不用关心,主线程也不会阻塞,还能继续发起新的请求。至于任务具体怎么处理,这些细节都是靠异步服务层的多线程异步并行来完成的,这些线程是一开始就创建的,不会因为大量的任务到来而创建新的线程,避免了频繁创建和销毁线程导致的系统开销,而且通过多核处理能大幅提高处理效率。

线程池实现的关键技术分析

上一节介绍了线程池的基本概念和基本结构,它是由三层组成:同步服务层、排队层和异步服务层,其中排队层居于核心地位,因为上层会将任务加到排队层中,异步服务层同时也会取出任务,这里有一个同步的过程。在实现时,排队层就是一个同步队列,允许多个线程同时去添加或取出任务,并且要保证操作过程是安全的。线程池有两个活动过程,一个是往同步队列中添加任务的过程,另一个是从同步队列中取任务的过程,如下图显示
在这里插入图片描述
从活动图中可以看到线程池的活动过程,一开始线程池会启动一定数量的线程,这些线程属于异步层,主要用来并行处理排队层中的任务,如果排队层中的任务数为空,则这些线程等待任务的到来,如果发现排队层中有任务了,线程池则会从等待的这些线程中唤醒一个来处理新任务。同步服务层则会不断地将新的任务添加到同步排队层中,这里有个问题值得注意,有可能上层的任务非常多,而任务又是非常耗时的,这时,异步层中的线程处理不过来,则同步排队层中的任务会不断增加,如果同步排队层不加上限控制,则可能会导致排队层中的任务过多,内存暴涨的问题。因此,排队层需要加上限的控制,当排队层中的任务数达到上限时,就不让上层的任务添加进来,起到限制和保护的作用。

线程池模型C++ 11实现

1. github示例代码地址

2. 实现代码

  1. 同步队列SyncQueue.hpp
#ifndef __SYNC_QUEUE__
#define __SYNC_QUEUE__

#include<list>
#include<mutex>
#include<thread>
#include<condition_variable>
#include <iostream>
using namespace std;

template<typename T>
class SyncQueue {
public:
    SyncQueue(int maxSize) :m_maxSize(maxSize), m_needStop(false) {}
    void Put(const T& x) {
        Add(x);
    }
    void Put(T&& x) {
        Add(std::forward<T>(x));
    }

    void Take(std::list<T>& list) {
        std::unique_lock<std::mutex> locker(m_mutex);
        m_notEmpty.wait(locker, [this] {return m_needStop || NotEmpty(); });
        if (m_needStop)
            return;
        list = std::move(m_queue);
        m_notFull.notify_one();
    }
    void Take(T& t){
        std::unique_lock<std::mutex> locker(m_mutex);
        m_notEmpty.wait(locker, [this] {return m_needStop || NotEmpty(); });
        if (m_needStop)
            return;
        t = m_queue.front();
        m_queue.pop_front();
        m_notFull.notify_one();
    }
    void Stop(){
        {
            std::lock_guard<std::mutex> locker(m_mutex);
            m_needStop = true;
        }
        m_notFull.notify_all();
        m_notEmpty.notify_all();
    }
    bool Empty(){
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.empty();
    }
    bool Full(){
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.size() == m_maxSize;
    }
    size_t Size(){
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.size();
    }
    int Count(){
        return m_queue.size();
    }
private:
    bool NotFull() const{
        bool full = m_queue.size() >= m_maxSize;
        //if (full)
        //    cout << "缓冲区满了,需要等待..." << endl;
        return !full;
    }
    bool NotEmpty() const{
        bool empty = m_queue.empty();
        //if (empty)
        //    cout << "缓冲区空了,需要等待...,异步层的线程ID: " << std::this_thread::get_id() << endl;
        return !empty;
    }
    template<typename F>
    void Add(F&& x){
        std::unique_lock< std::mutex> locker(m_mutex);
        m_notFull.wait(locker, [this] {return m_needStop || NotFull(); });
        if (m_needStop)
            return;
        m_queue.push_back(std::forward<F>(x));
        m_notEmpty.notify_one();
    }
private:
    std::list<T> m_queue;                  // 缓冲区
    std::mutex m_mutex;                    // 互斥量和条件变量结合起来使用
    std::condition_variable m_notEmpty;    // 不为空的条件变量
    std::condition_variable m_notFull;     // 没有满的条件变量
    unsigned int m_maxSize;                // 同步队列最大的size
    bool m_needStop;                       // 停止的标志
};

#endif // !__SYNC_QUEUE__
  1. 线程池ThreadPool.hpp
#ifndef __THREAD_POOL__
#define __THREAD_POOL__

#include<list>
#include<thread>
#include<functional>
#include <type_traits>
//#include <function_traits>
#include<memory>
#include <atomic>
#include "SyncQueue.hpp"

const int MaxTaskCount = 100;

template <typename R = void>
class ThreadPool {
public:
    using Task  = std::function<R()>;

public:
    ThreadPool(int numThreads = std::thread::hardware_concurrency()) : m_queue(MaxTaskCount) {
        Start(numThreads);
    }
    ~ThreadPool(void) {
        // 如果没有停止时则主动停止线程池
        Stop();
    }
    void Stop() {
        // 保证多线程情况下只调用一次StopThreadGroup
        std::call_once(m_flag, [this] {StopThreadGroup(); });
    }

public:
    void AddTask(Task&& task) {
        m_queue.Put(std::forward<Task>(task));
    }
    void AddTask(const Task& task) {
        m_queue.Put(task);
    }

    template<typename Fun,typename Arg ,typename... Args>
    void AddTask(Fun&& f, Arg&& arg,Args&&... args) {
        auto f_task = std::bind(std::forward<Fun>(f), std::forward<Arg>(arg),std::forward<Args>(args)...);
        m_queue.Put(std::move(f_task));
    }
public:
    void Start(int numThreads) {
        m_running = true;
        // 创建线程组
        for (int i = 0; i < numThreads; ++i) {
            m_threadgroup.push_back(std::make_shared<std::thread>(&ThreadPool::RunInThread, this));
        }
    }

    void RunInThread() {
        while (m_running) {
            // 取任务分别执行
            std::list<Task> list;
            m_queue.Take(list);
            for (auto& task : list) {
                if (!m_running)
                    return;
                task();
            }
        }
    }
    void StopThreadGroup() {
        m_queue.Stop();                				 	// 让同步队列中的线程停止
        m_running = false;             					 // 置为false,让内部线程跳出循环并退出
        for (auto thread : m_threadgroup) {				// 等待线程结束
            if (thread)
                thread->join();
        }
        m_threadgroup.clear();
    }

private:
    std::list<std::shared_ptr<std::thread>> m_threadgroup;  // 处理任务的线程组
    SyncQueue<Task> m_queue;                                      // 同步队列
    atomic_bool m_running;                                      // 是否停止的标志
    std::once_flag m_flag;
};


#endif // !__THREAD_POOL__
  1. 测试
#include<string>
#include <iostream>
#include "ThreadPool.hpp"

using namespace std;

int add_fun1(int n) {
    std::cout << "commonfun:add_fun1:n=" << n << std::endl;
    return n + 1;
}

double add_fun2(int n) {
    std::cout << "commonfun:add_fun2:n=" << n << std::endl;
    return n + 1.0;
}
void TestThdPool(){
    {
        ThreadPool<> pool;
        pool.Start(2);
        std::thread thd1([&pool] {
            for (int i = 0; i < 1; i++) {
                auto thdId = this_thread::get_id();
                pool.AddTask([thdId] {
                    cout << "lambda表达式:同步层线程1的线程ID:" << thdId << endl; });
            }});

        std::thread thd2([&pool] {
            auto thdId = this_thread::get_id();
            pool.AddTask([thdId] {
                cout << "lambda表达式:同步层线程2的线程ID:" << thdId << endl; });
            
            std::function<int()> f = std::bind(add_fun1, 1);
            pool.AddTask(f);//其实参数应该是std::function<void()>
            
            pool.AddTask(add_fun2, 1);
            
            });
        this_thread::sleep_for(std::chrono::seconds(2));
        pool.Stop();
        thd1.join();
        thd2.join();
    }

    {
        ThreadPool<int> pool;
        pool.Start(1);
        std::thread thd1([&pool] {
            auto thdId = this_thread::get_id();
            auto f = std::bind(add_fun1, 1);
            pool.AddTask(f);
        });

        this_thread::sleep_for(std::chrono::seconds(2));
        pool.Stop();
        thd1.join();
    }

    {
        ThreadPool<double> pool;
        pool.Start(1);
        std::thread thd1([&pool] {
            auto thdId = this_thread::get_id();
            pool.AddTask(add_fun2,1);
        });

        this_thread::sleep_for(std::chrono::seconds(2));
        pool.Stop();
        thd1.join();
    }
}
int main() {
    TestThdPool();
    return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!