webrtc執行緒模型分析

sh_dd發表於2020-12-12

目錄

 

概述

1.ProcessThreadImpl類

2.TaskQueue類

3.Thread類


概述

webrtc是一個跨平臺的實時音訊通訊技術,底層對不同平臺的執行緒介面進行了封裝,本文以windows平臺為例,簡要分析一下其封裝的執行緒類的特點和使用方法。在90版本的webrtc,封裝了三個執行緒相關的類,分別是modules/utility/source/ProcessThreadImpl、rtc_base/TaskQueue、rtc_base/Thread,這三個類建立執行緒都是通過呼叫CreateThread函式來實現,webrtc使用執行緒基本上都是通過這三個類來進行的。

個人感覺webrtc執行緒類的最大特點就是支援線上程間傳遞訊息處理函式,例如執行緒A想讓執行緒B乾點事情,那麼就向執行緒B傳送一個任務,這個任務裡面就包含了需要線上程B內執行的訊息處理函式,這個處理函式往往是lambda函式(當然也可以是全域性的函式)。這種設計最大好處的就是簡化了執行緒間進行通訊的方法,而這又是通過lambda函式的引數捕獲機制來實現的。舉個例子當我們需要向另一個執行緒傳遞多個變數的時候,往往都是通過設計多個引數或者定義一個結構體來實現,在傳遞資料前可能還需要一個個的進行變數賦值,但是通過lambda函式一切就變得簡單了,使用lambda函式的捕獲列表功能,目標執行緒處理訊息時需要什麼引數就可以直接在捕獲列表傳進去,甚至還可以把this指標也給傳進去,然後再在lambda函式內部呼叫類的成員函式。當然了,使用這種執行緒通訊模型需要十分清楚類的成員變數會在哪些執行緒被訪問、那些地方需要加鎖進行保護。

1.ProcessThreadImpl類

ProcessThread執行緒提供的函式是Start、Stop、WakeUp、RegisterModule、DeRegisterModule,可以向執行緒註冊多個處理模組,這些模組會線上程內被頻繁定時呼叫。ProcessThread主要用於建立需要迴圈處理訊息型別的執行緒,例如webrtc中就通過ProcessThread建立了PacerThread、ModuleProcessThread執行緒,其中PacerThread執行緒用於平滑網路資料包的傳送,而ModuleProcessThread用於處理rtp/rtcp訊息等。

2.TaskQueue類

TaskQueue主要提供了PostTask、PostDelayedTask介面。PostTask、PostDelayedTask可以將一個lambda函式作為引數傳入,在lambda函式內部也可以呼叫包含lambda函式的函式,這樣就看下來一個類的成員函式會被不同的執行緒所呼叫,如下面的函式:

void VideoStreamEncoder::OnLossNotification(
    const VideoEncoder::LossNotification& loss_notification) {
  if (!encoder_queue_.IsCurrent()) {
    encoder_queue_.PostTask(
        [this, loss_notification] { OnLossNotification(loss_notification); });
    return;
  }

  RTC_DCHECK_RUN_ON(&encoder_queue_);
  if (encoder_) {
    encoder_->OnLossNotification(loss_notification);
  }
}

TaskQueue使用了TaskQueueBase型別的例項。TaskQueue沒有實現不同執行緒間的同步呼叫,PostTask把訊息發到目標執行緒後會立即返回,不會等待目標執行緒處理完訊息,而rtc_base/Thread類的Send/Invoke函式就會等待。

3.Thread類

Thread裡面的Send/Invoke函式可以實現兩個不同執行緒之間的同步呼叫,例如A執行緒呼叫B執行緒的一個函式F,函式F是執行在B執行緒裡面,但是A執行緒在Invoke後會阻塞,等到B執行緒執行完F後A執行緒才繼續往下執行。實現原理是在Invoke內部呼叫了PostTask,在呼叫PostTask時傳入了一個lambda函式,這個lambda函式以引用方式捕獲了一個區域性變數ready,ready在目標執行緒執行完訊息處理函式後會被設定為true,而呼叫者執行緒在PostTask後迴圈等待ready,只有ready等於true後才結束迴圈,從而函式返回,這樣就實現了以同步方式呼叫另一個執行緒的函式。程式碼如下:

 bool ready = false;
  PostTask(
      webrtc::ToQueuedTask([msg]() mutable { msg.phandler->OnMessage(&msg); },
                           [this, &ready, current_thread] {
                             CritScope cs(&crit_);
                            // 執行緒B執行
                             ready = true;
                             current_thread->socketserver()->WakeUp();
                           }));

  bool waited = false;
  crit_.Enter();
  // 執行緒A執行
  while (!ready) {
    crit_.Leave();
    current_thread->socketserver()->Wait(kForever, false);
    waited = true;
    crit_.Enter();
  }
  crit_.Leave();

Thread繼承自TaskQueueBase,Thread類主要提供了三個傳送訊息的函式Post、Send、Invoke,其中Post不會阻塞,而Send/Invoke會阻塞,Post/Send可以傳入訊息處理物件,而Invoke可以傳入訊息處理函式(lambda函式)。

 

全文完。

相關文章