簡單的執行緒池(七)

cnblogs發表於2021-12-23

◆ 概要

本文中,筆者為 《簡單的執行緒池(四)》 提及的非阻塞獨佔式執行緒池增加了一項功能:當某個工作執行緒的任務佇列中無工作任務時,此工作執行緒可以去其他工作執行緒的任務佇列中獲取任務。筆者稱之為非阻塞互助式執行緒池。

gist

筆者對比了測試結果與 《簡單的執行緒池(六)》 的資料,得出了新增功能前後的差異。

本文不再贅訴與 《簡單的執行緒池(四)》 相同的內容。如有不明之處,請參考該部落格。

◆ 實現

以下程式碼給出了此執行緒池的實現,(lockwise_mutual_pool.h)

class Thread_Pool {

  private:

    struct Task_Wrapper { ...
    
    };

    atomic<bool> _suspend_;
    atomic<bool> _done_;
    unsigned _workersize_;
    thread* _workers_;
    Lockwise_Queue<Task_Wrapper>* _workerqueues_;

    void work(unsigned index) {
        Task_Wrapper task;
        while (!_done_.load(memory_order_acquire)) {
            if (_workerqueues_[index].pop(task))
                task();
            else                                                     // # 1
                for (unsigned i = 0; i < _workersize_; ++i)
                    if (_workerqueues_[(index + i + 1) % _workersize_].pop(task)) {    // #2
                        task();
                        break;
                    }
            while (_suspend_.load(memory_order_acquire))
                std::this_thread::yield();
        }
    }

    void stop() { ...

    }

  public:
    Thread_Pool() : _suspend_(false), _done_(false) { ....

    }
    ~Thread_Pool() { ...
    
    }

    template<class Callable>
    future<typename std::result_of<Callable()>::type> submit(Callable c) { ...
    
    }

};

此執行緒池的程式碼與 非阻塞獨佔式執行緒池 相比,僅在工作執行緒的初始函式 Thread_Pool::work(unsigned) 上有區別。當某個工作執行緒的任務佇列中無工作任務時(#1),此工作執行緒會去其他工作執行緒的任務佇列中獲取任務。這裡涉及到的“選擇其他工作執行緒的任務佇列的演算法”(#2),來自於本文最後的參考書籍。

◆ 邏輯

此執行緒池的結構和與 《簡單的執行緒池(四)》 中的一致,此處略。

此執行緒池使用者提交任務與工作執行緒執行任務的併發過程與 《簡單的執行緒池(一)》 中的一致,此處略。

◆ 驗證

驗證過程採用了 《簡單的執行緒池(三)》 中定義的的測試用例。筆者對比了測試結果與 《簡單的執行緒池(六)》 的資料,結果如下,

圖1 列舉了 吞吐量1的差異 在 0.5 分鐘、1 分鐘和 3 分鐘的提交週期內不同思考時間上的對比。

圖1

【注】非阻塞互助式 略稱為 LM ,下同。

可以看到,

  • 當思考時間為 0 時,LM 的吞吐量優於 LS、BS 的吞吐量,相當於 LU、BU 的吞吐量;延長提交週期後,LM 和 LS、BS、LU、BU 的吞吐量差異沒有發生明顯變化;
  • 當思考時間不為 0 時,LM 的吞吐量大幅優於 LS、LU 的吞吐量,大幅劣於 BS、BU 的吞吐量,但差異不會因提交週期的延長而大幅變化;隨著思考時間的增加,LM 的吞吐量與 LS、BS、LU、BU 的吞吐量差異逐漸消失。

圖2 列舉了 吞吐量2的差異 在 0.5 分鐘、1 分鐘和 3 分鐘的提交週期內不同思考時間上的對比。

圖2

可以看到,

  • 當思考時間為 0 時,LM 的吞吐量優於 LS、BS、LU、BU 的吞吐量;延長提交週期後,LM 和 LS、BS、LU、BU 的吞吐量差異沒有發生明顯變化;
  • 當思考時間不為 0 時,因 LM 和 LS、BS、LU、BU 的吞吐量均為 0,它們間沒有差異。

圖3 列舉了 吞吐量3的差異 在 0.5 分鐘、1 分鐘和 3 分鐘的提交週期內不同思考時間上的對比。

圖3

可以看到,

  • 當思考時間為 0 時,LM 的吞吐量相當於 LS、BS、LU 的吞吐量,略劣於 BU 的吞吐量;延長提交週期後,LM 和 LS、BS、LU、BU 的吞吐量差異沒有發生明顯變化;
  • 當思考時間不為 0 時,LM 的吞吐量大幅優於 LS、LU 的吞吐量,大幅劣於 BS、BU 的吞吐量,但差異不會因提交週期的延長而大幅變化;隨著思考時間的增加,LM 的吞吐量與 LS、BS、LU、BU 的吞吐量差異逐漸消失。

基於以上的對比分析,筆者認為,在非阻塞式執行緒池中,
  • 互助式的吞吐量指標優於共享式、獨佔式的吞吐量指標。

◆ 最後

完整的示例程式碼和測試結果請參考 [github] cnblogs/15710614

關於“選擇其他工作執行緒的任務佇列的演算法”,筆者參考了 C++併發程式設計實戰 / (美)威廉姆斯 (Williams, A.) 著; 周全等譯. - 北京: 人民郵電出版社, 2015.6 (2016.4重印) 一書。致 Anthony Williams、周全等譯者。

相關文章