brpc的bthread解讀
- 2020 年 4 月 7 日
- 筆記
一、Bthread的簡單使用
std::vector<bthread_t> bids; for (int i = 0; i < FLAGS_thread_num; ++i) { if (bthread_start_background(&bids[i], NULL, myfunc, &myarg) != 0) { LOG(ERROR) << "Fail to create bthread"; return -1; } } for (int i = 0; i < FLAGS_thread_num; ++i) { bthread_join(bids[i], NULL); }
- bthread_start_background 相當於pthread_create,此外還有bthread_start_urgent,這個urgent函數用於程式碼更需要緊急執行的場景,他會把舊的執行緒置入調度隊列里。
- bthread_join 相當於pthread_join嗎
二、bthread的原理
bthread是brpc使用的M:N執行緒庫,M個bthread會映射至N個pthread。
在我們調用一次bthread_start_background / bthread_start_urgent會依次觸發TaskControl和TaskGroup的相應介面。bthread主要的類有兩個TaskControl和TaskGroup,下面就來探究下TaskControl和TaskGroup如何實現M:N的執行緒模型。
2.1、 bthread進入TaskControl
首先Task Control使用單例模式。
get_or_new_task_control 獲取到Task Control實例,並且確保了唯一Task Control的實例,在改函數中該實例嚴格的生成用atomic保證,並且使用記憶體memory_order_consume保證了程式碼順序不被編譯器優化,確保了在多執行緒環境下的執行順序。
2.2、 TaskControl
TaskControl採用單例模式,對TaskGroup進行管理。
- Task Control可以創建多個Task Group,這裡用concurrency並發度表示多個Task Gruop。程式碼中檢驗是否的TaskControl創建並初始化成功的依據是判butil::atomic<int> _concurrency是一個有效的值。默認個數是9
- Task Control還會創建一個定時器執行緒。brpc採用condition_variable的喚醒方式+牆上時鐘+小頂堆的方式實現其定時器。
- Timerthread負責執行定時器喚醒並執行定時任務
那麼Task Control管理了什麼?以下圖示是task control的介面:
介面主要分為兩大類:一類是創建並管理TaskGroup,一類是統計TaskGroup相關的數據:
- 創建管理Task Group,包括add_worker,choose_one_group,stop_and_join,delete_task_group
- 統計taskgroup的數據包括get_cumulated_worker_time,get_cumulated_switch_count,get_cumulated_signal_count,print_rq_sizes_in_the_tc
- steal_worker(TaskGroup在自身隊列中沒有任務情況下,搶佔其他task group的內核pthread資源)
- signal_task,喚醒沒有任務在等待的TaskGroup處理任務
- TaskControl管轄的Pthread之外的Pthread創建的Bthread,由TaskControl 隨機選一個TaskGroup進行bthread投遞。
2.3、TaskGroup
- TaskGroup則1:1對應pthread
- 他是1:N的 bthread調度器。一個TaskGroup是一個pthread,但是可以管理多個歸屬於這個TaskGroup的所有bthread Task,
- 一個task_group維護者一個run_queue和一個remote_queue。Remote_queue:用於存放非TaskControl中執行緒創建的Bthread
備份二級隊列, 向隊列中提交不在btrhead中創建的任務
2.3.1 入口函數run_main_task()
TaskGroup::run_main_task() 這個是TaskGroup的main入口。
while(Wait_task()) { //按照順序先從自身的run_queue,然後去remote_queue找任務執行,或者steal的方式調度任務 TaskGroup::sched_to(&dummy,tid); }