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

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