brpc的bthread解讀

一、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的執行緒模型。

圖1、TaskControl和TaskGroup的調用鏈

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 TaskControl的介面

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);  }