C++Helper--使用C++11实现半同步半异步线程池,接口接受:函数对象、成员函数、普通函数、fucntion和lamda表达式等。

  本文使用C++11的线程、互斥量和条件变量,实现了一个轻巧的线程池,可用于大量并发任务的场景,以避免频繁的线程创建和销毁,节约系统资源。

  本文对处理任务的接口,也进行了包装,可接受:函数对象、成员函数、普通函数、fucntion和lamda表达式,便于完美的应用到实际工作中。

  本文源码见【完整代码】章节,或GitHub:https://github.com/deargo/cpphelper

线程池介绍

  在处理大量并发任务的时候,如果按照传统的方式,一个请求一个线程来处理请求任务,大量的线程创建和销毁,将消耗过多的系统资源,还增加了线程上下文切换的开销,而通过线程池技术,就可以很好的解决这些问题。

  线程池技术通过在系统中预先创建一定数量的线程,当任务请求到来时从线程池中,分配一个预先创建的线程去处理任务,线程在完成任务之后,还可以重用,不会销毁,而是等待下次任务的到来。

  线程池分为半同步半异步线程池和领导者追随者线程池,本文将介绍的是前者。半同步半异步线程池分为三层:

  • 同步服务层:它处理来自上层的任务请求,上层的请求可能是并发的,这些请求不是马上就会被处理的,而是将这些任务放到一个同步排队层中,等待处理。
  • 同步排队层:来自上层的任务请求,都会加到排队层中,等待处理。
  • 异步服务层:这一层中会有多个线程,同时处理排队层中的任务,异步服务层从同步排队层中取出任务,并行的处理。

关键技术分析

  通过上一节我们知道,排队层居于核心地位,因为上层会将任务添加到排队层中,下层同时也会取出任务,这里有一个同步的过程。

  在实现时,排队层就是一个同步队列,允许多个线程同时去添加,或取出任务,并且要保障操作过程是安全的。

  线程池有两个活动过程,一个是往同步队列中添加任务的过程,另一个是从同步队列中取出任务的过程。

  线程池需启动一定数量的异步线程,来并行处理排队层中的任务,如果排队层中的任务为空,则需等待任务的到来,如果发现有新任务,则会唤醒一个线程来处理。

  同步服务层会不断的将新的任务,添加到同步排队层中。当任务非常多又耗时时,异步线程处理不过来,则会导致排队层任务过多,内存暴涨,因此需要添加上限,避免此问题。

  另外,对于任务类型,与线程池,本是不相关的,但考虑到实际运用,任务类型应支持:函数对象、成员函数、普通函数、fucntion和lamda表达式等,于是结合我的另外一篇博客《C++Helper--用C++11改进命令模式》,可对添加任务的接口,进行优化。

完整代码

#pragma once

#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif

#include <list>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <iostream>
#include <memory>
#include <atomic>
#include <functional>

using namespace std;

namespace CppHelper
{
class CThreadPool
{
private:
    using Locker = std::unique_lock<std::mutex>;
    using Guard = std::lock_guard<std::mutex>;

    //同步队列
    template <typename Task>
    class SyncQueue
    {
    public:
        using Tasks = std::list<Task>;

        SyncQueue(int maxSize): m_maxSize(maxSize), m_needStop(false) { }
        ~SyncQueue(){ resize(0);}

        //添加事件
        bool put(const Task& task)
        {
            return add(task);
        }

        //添加事件
        bool put(Task && task)
        {
            //调用内部接口,进行完美转发
            return add(std::forward<Task>(task));
        }

        //从队列中取事件,取所有事件
        bool take(Tasks &tasks)
        {
            Locker locker(m_mutex);
            //当不满足任何一个则等待,但是若m_needStop为true是因为任务要终止了所以不阻塞
            m_notEmpty.wait(locker, [this]{return (m_needStop || notEmpty()); });
            if (m_needStop)
            {
                return false;
            }

            tasks = std::move(m_tasks);
            m_notFull.notify_one();
            return true;
        }

        //取一个事件
        bool take(Task &task)
        {
            Locker locker(m_mutex);
            m_notEmpty.wait(locker, [this]{return m_needStop || notEmpty(); });
            if (m_needStop)
            {
                return false;
            }

            task = m_tasks.front();
            m_tasks.pop_front();
            m_notFull.notify_one();
            return true;
        }

        //终止同步队列
        void stop()
        {
            {
                //锁作用域就在这对大括号内
                Guard guard(m_mutex);
                //将终止标志设为true
                m_needStop = true;
            }

            //唤醒所有进程一一终止
            m_notFull.notify_all();
            m_notEmpty.notify_all();
        }

        //队列为空
        bool empty()
        {
            Guard guard(m_mutex);
            return m_tasks.empty();
        }

        //队列为满
        bool full()
        {
            Guard guard(m_mutex);
            return m_tasks.size() == m_maxSize;
        }

        //队列容量
        typename Tasks::size_type capacity()
        {
            Guard guard(m_mutex);
            return m_maxSize;
        }

        //队列大小
        typename Tasks::size_type size()
        {
            Guard guard(m_mutex);
            return m_tasks.size();
        }

        //重置队列大小
        void resize(int maxSize)
        {
            stop();
            Locker locker(m_mutex);
            m_tasks.clear();
            m_maxSize = maxSize;
        }

        //清空队列
        void clear()
        {
            stop();
            Locker locker(m_mutex);
            m_tasks.clear();
        }

    private:
        //队列不为满
        bool notFull() const
        {
            bool full = (m_tasks.size() >= m_maxSize);
            if (full)
            {
                cout << "the queue is full, need wait..." << endl;
            }

            return !full;
        }

        //队列不为空
        bool notEmpty() const
        {
            bool empty = m_tasks.empty();
            if (empty)
            {
                cout << "asynchronous thread["<<this_thread::get_id() << "]: the queue is empty, need wait." << endl;
            }

            return !empty;
        }

        //向队列中添加任务,若不为满且终止标志为false则添加事件
        bool add(Task && task)
        {
            Locker locker(m_mutex);
            //当不满足任何一个则等待,但是若m_needStop为true是因为任务要终止了所以不阻塞
            m_notFull.wait(locker, [this]{return m_needStop || notFull(); });
            if (m_needStop)
            {
                return false;
            }

            m_tasks.emplace_back(std::forward<Task>(task));
            m_notEmpty.notify_one();
            return true;
        }

    private:
        //缓冲区
        Tasks m_tasks;
        //互斥量和条件变量结合起来使用
        std::mutex m_mutex;
        //队列不为空的条件变量
        std::condition_variable m_notEmpty;
        //队列不为满的条件变量
        std::condition_variable m_notFull;
        //同步队列最大长度
        size_t m_maxSize;
        //终止的标识,当为true时代表同步队列要终止
        bool m_needStop;
    };

private:
    using Ret = void;
    using Task = std::function<Ret()>;
    using Tasks = std::list<Task>;

public:
    //传递给同步队列的最大个数
    enum { DEFAULT_MAX_TASK_COUNT = 1000 };

    //构造函数,默认处理线程数为CPU核心数量
    CThreadPool(int handleTheadCount = std::thread::hardware_concurrency()):
        m_handle_count(handleTheadCount),m_queue(CThreadPool::DEFAULT_MAX_TASK_COUNT)
    {
        start();
    }

    ~CThreadPool()
    {
        //如果没有停止,则主动停止线程池
        stop();
    }

    //启动线程池(默认已启动)
    void start()
    {
        Locker locker(m_mutex);
        if(m_running)
        {
            return;
        }
        run();
    }

    //终止所有任务的执行
    void stop()
    {
        Locker locker(m_mutex);
        if(false == m_running)
        {
            return;
        }

        //终止同步队列
        m_queue.stop();
        m_running = false;

        for (auto thread : m_threadgroup)
        {
            if (thread)
            {
                thread->join();
            }
        }
        m_threadgroup.clear();
    }

    //添加任务:接受返回类型为void类型的function、函数对象、lamda和普通函数
    template< class Func, class... Args, class = typename std::enable_if<!std::is_member_function_pointer<Func>::value>::type>
    bool addTask(Func && func, Args && ... args)
    {
        return m_queue.put([&func, args...]{return func(args...); });
    }

    //添加任务:接受返回类型为void类型的const成员函数
    template<class CObj, class... DArgs, class PObj, class... Args>
    bool addTask(Ret(CObj::*func)(DArgs...) const, PObj && pObj, Args && ... args)
    {
        return m_queue.put([&, func]{return (*pObj.*func)( args...); });
    }

    //添加任务:接受返回类型为void类型的non-const类型成员函数
    template<class CObj, class... DArgs, class PObj, class... Args>
    bool addTask(Ret(CObj::*func)(DArgs...), PObj && pObj, Args && ... args)
    {
        return m_queue.put([&, func]{return (*pObj.*func)( args...); });
    }

    //队列接口
    bool queueEmpty()  { return m_queue.empty();}
    bool queueFull()  { return m_queue.full();}
    typename Tasks::size_type queueCapacity()  { return m_queue.capacity();}
    typename Tasks::size_type queueSize()  { return m_queue.size();}
    void queueResize(int maxSize)  { return m_queue.resize(maxSize);}
    void queueClear()  { return m_queue.clear();}

    //处理接口
    bool isRunning() { Guard guard(m_mutex); return m_running;}
    int handleCount() {Guard guard(m_mutex); return  m_handle_count;}
    void handleCountResize(int handleTheadCount)
    {
        stop();
        Locker locker(m_mutex);
        m_handle_count = handleTheadCount;
        run();
    }


private:
    void run()
    {
        m_running  = true;
        //建立numThreads个数的线程组
        for (int i = 0; i < m_handle_count; i++)
        {
            //多个线程依次的处理
            m_threadgroup.emplace_back(std::make_shared<std::thread>(&CThreadPool::runInThread, this));
        }
    }
    void runInThread()
    {
        while (m_running)
        {
            Tasks list;
            m_queue.take(list);            
            //取出任务队列中的全部,依次执行
            for (auto & task : list)
            {
                if (!m_running)
                {
                    return ;
                }
                //执行任务
                task();
            }
        }
        return;
    }

private:
    std::mutex m_mutex;
    int m_handle_count;

    //处理任务的线程组
    std::list<std::shared_ptr<std::thread>> m_threadgroup;
    //同步队列
    SyncQueue<Task> m_queue;
    //运行的标志,flase代表终止
    atomic_bool m_running;
    //保证在函数在多线程环境中只被调用一次
    std::once_flag m_flag;
};

}

测试示例

  在本示例中,线程池初始将创建两个线程,然后外部线程将不停的向线程池中,添加新任务,线程池中的异步线程,将会并行的处理同步队列中的任务。

测试代码

    CppHelper::CThreadPool pool(2);

    std::thread thd1([&pool]
    {
        auto thdid = this_thread::get_id();
        for (int i = 0; i < 10; i++)
        {
            pool.addTask([](std::thread::id thdid, int value)
            {
                cout << "synchronous thread1[" << thdid << "] value[" << value << "], handle thread: " << this_thread::get_id() << endl;
            },thdid,i+100);
        }
    });
    std::thread thd2([&pool]
    {
        auto thdid = this_thread::get_id();
        for (int i = 0; i < 10; i++)
        {
            pool.addTask([&](std::thread::id thdid, int value)
            {
                cout << "synchronous thread2[" << thdid << "] value[" << value << "], handle thread: " << this_thread::get_id() << endl;
            },thdid,i+200);
        }
    });

    cout << "sleep seconds(2) ..." << endl << endl;
    this_thread::sleep_for(std::chrono::seconds(2));

    cout << endl << "input any words to stop: ";
    getchar();
    pool.stop();
    thd1.join();
    thd2.join();

测试结果

参考资料及源码:

  参考书籍:《深入应用C++11:代码优化与工程级应用》。

  完整源码:https://github.com/deargo/cpphelper

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章