1、概述
本篇主要是簡單介紹WebRTC中的線程,WebRTC源碼對線程做了很多的封裝。
1.1 WebRTC中線程的種類
1.1.1 信令線程
用于與應用層的交互,比如創建offer,answer,candidate等絕大多數的操作
1.1.2 工作線程
負責內部的處理邏輯,比如音視頻編解碼、采集、渲染這些操作,平滑處理,質量監測
1.1.3 網絡線程
負責網絡數據包的轉發,工作線程對音視頻數據進行編碼后,發送編碼后的音視頻數據到網絡。
從網絡中接收音視頻數據包,然后交給工作線程進行解碼,丟包處理等操作
1.2 peerconnection_client
peerconnection_client一共創建了5個線程,分別為
主線程:windows中main函數的,處理窗口消息的線程
與信令服務器連接處理的線程:PhysicalSocketServer關聯
信令線程:PhysicalSocketServer關聯
工作線程:NullSocketServer關聯
網絡線程:PhysicalSocketServer關聯
1.3?線程類關系圖
1.4 線程類
class RTC_LOCKABLE RTC_EXPORT Thread : public TaskQueueBase {public:explicit Thread(SocketServer* ss);SocketServer* socketserver();bool Start();virtual void Stop();virtual void Run();bool ProcessMessages(int cms);protected:void PostTaskImpl(absl::AnyInvocable<void() &&> task,const PostTaskTraits& traits,const Location& location) override;private:absl::AnyInvocable<void() &&> Get(int cmsWait);void Dispatch(absl::AnyInvocable<void() &&> task);#if defined(WEBRTC_WIN)static DWORD WINAPI PreRun(LPVOID context);
#elsestatic void* PreRun(void* pv);
#endifstd::queue<absl::AnyInvocable<void() &&>> messages_ RTC_GUARDED_BY(mutex_);std::priority_queue<DelayedMessage> delayed_messages_ RTC_GUARDED_BY(mutex_);SocketServer* const ss_;
#if defined(WEBRTC_POSIX)pthread_t thread_ = 0;
#endif
#if defined(WEBRTC_WIN)HANDLE thread_ = nullptr;DWORD thread_id_ = 0;
#endif
};
- 成員變量 thread_ 在不同平臺有不同的實現,win下采用線程內核對象創建,linux下采用pthread創建
- 成員變量 messages_ 相當于里面保存的是函數對象,只不過用absl庫,我們可以把它當做是std::function函數對象
- 線程之間通信,通過PostTask函數進行,相當于線程1需要執行的任務放入線程2的queue,然后線程2不斷從queue讀取任務執行,這樣來進行通信
1.5 線程管理類?
class RTC_EXPORT ThreadManager {public:static ThreadManager* Instance();static void Add(Thread* message_queue);static void Remove(Thread* message_queue);Thread* CurrentThread();void SetCurrentThread(Thread* thread);private:// This list contains all live Threads.std::vector<Thread*> message_queues_ RTC_GUARDED_BY(crit_);#if defined(WEBRTC_POSIX)pthread_key_t key_;
#endif#if defined(WEBRTC_WIN)const DWORD key_;
#endif
};
線程管理類ThreadManager用到一個技術點是線程本地存儲(TLS),需要理解這個技術點,否則比較難讀懂WebRTC源碼中線程這塊的代碼。
1.5.1 線程本地化存儲(TLS)
#include <windows.h>
#include <stdio.h>// 全局 TLS 索引
DWORD tlsIndex;// 線程函數
DWORD WINAPI ThreadFunction(LPVOID lpParam) {int threadNum = *(int*)lpParam;char buffer[256];// 為當前線程設置 TLS 值sprintf_s(buffer, "線程 %d 的私有數據", threadNum);TlsSetValue(tlsIndex, strdup(buffer)); // 注意:使用 strdup 分配內存// 獲取并打印當前線程ID和TLS值DWORD threadId = GetCurrentThreadId();char* data = (char*)TlsGetValue(tlsIndex);printf("線程 ID=%lu, 編號=%d, TLS 數據: %s\n", threadId, threadNum, data);// 清理分配的內存free(data);return 0;
}int main() {// 分配 TLS 索引tlsIndex = TlsAlloc();if (tlsIndex == TLS_OUT_OF_INDEXES) {printf("TlsAlloc 失敗,錯誤碼: %d\n", GetLastError());return 1;}// 在主線程中設置并獲取 TLS 值char mainBuffer[256];sprintf_s(mainBuffer, "主線程的私有數據");TlsSetValue(tlsIndex, strdup(mainBuffer));// 打印主線程ID和TLS值DWORD mainThreadId = GetCurrentThreadId();char* mainData = (char*)TlsGetValue(tlsIndex);printf("主線程 ID=%lu, TLS 數據: %s\n", mainThreadId, mainData);// 創建兩個線程int thread1Num = 1;int thread2Num = 2;HANDLE hThread1 = CreateThread(NULL, 0, ThreadFunction, &thread1Num, 0, NULL);HANDLE hThread2 = CreateThread(NULL, 0, ThreadFunction, &thread2Num, 0, NULL);// 等待線程結束WaitForSingleObject(hThread1, INFINITE);WaitForSingleObject(hThread2, INFINITE);// 關閉線程句柄CloseHandle(hThread1);CloseHandle(hThread2);// 清理主線程分配的內存free(TlsGetValue(tlsIndex));// 釋放 TLS 索引TlsFree(tlsIndex);return 0;
}
在每個線程中保存的value不一樣,每個線程取出來的value就是不一樣,通俗地說,假設我們有3個線程,分別為Thread1、Thread2、Thread3, 分別保存value為1、2、3,那么如果當前如果在Thread2運行的代碼里面打印value,那么打印的就是2。通過這樣的原理,假如當前是信令線程,通過ThreadManager的函數CurrentThread那么返回的就是信令線程,如果需要在網絡線程中運行,則需要利用PostTask放入到網絡線程中使用,我們可以看到WebRTC源碼很多都是用這個來進行判斷。