WebRTC学习进阶之路 --- 十四、源码分析之WebRTC中的线程详解-ThreadManager&Thread

本文深入剖析WebRTC中的线程管理,包括ThreadManager的单例模式、线程创建与管理,以及Thread类的构造、启动、消息循环和Invoke跨线程同步执行机制。详细讲解了ThreadManager的Instance、WrapCurrentThread、UnwrapCurrentThread和IsMainThread等方法,以及Thread的Create、Run和Send功能,帮助理解WebRTC的线程模型和多线程同步。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

WebRTC学习进阶之路系列总目录:https://blog.youkuaiyun.com/xiaomucgwlmx/article/details/103204274

前言

WebRTC源码中的很多注释是很赞的,看源码的时候多加阅读注释有助于更好的理解。
WebRTC实现了跨平台(Windows,MacOS,Linux,IOS,Android)的线程类rtc::Thread,WebRTC内部的network_thread,worker_thread,signaling_thread均是该类的实例。

基础功能
rtc:: Thread及其相关类,ThreadManager、MessageQueue,Runnable等等一起提供了如下的基础功能:
线程的管理:通过ThreadManager单例对象,可以管理所有的Thread实例;
线程的常规基本功能:rtc:: Thread提供创建线程对象,设置线程名称,启动线程去执行用户代码;
消息循环,消息投递:rtc:: Thread通过继承MessageQueue类,提供了内部消息循环,并提供了线程间异步,同步投递消息的功能;
跨线程执行方法:提供了跨线程执行方法,并返回执行结果的功能。该功能非常强大,因为WebRTC在某些功能模块的使用上,有要求其必需在指定的线程中才能调用的基本要求,比如音频模块:ADM 的创建必须要在 WebRTC 的 worker thread 中进行;
多路分离器:通过持有SocketServer对象,实现了多路分离器的功能,能处理网络IO;

          

signaling_thread:处理小工作量方法,要求此线程内的方法都必须快速返回。

worker_thread:处理大工作量的方法,此线程内的方法可能会处理很长时间。

network_thread:处理网络消息。
下边我们来看下两个线程的核心类:ThreadManager和Thread。

一、ThreadManager

ThreadManager的源码位于rtc_base目录下的thread.h与thread.cc中,相关头文件如下:

class RTC_EXPORT ThreadManager {
 public:
  static const int kForever = -1;

  // Singleton, constructor and destructor are private.
  static ThreadManager* Instance();

  Thread* CurrentThread();
  void SetCurrentThread(Thread* thread);
  Thread* WrapCurrentThread();
  void UnwrapCurrentThread();

  bool IsMainThread();

 private:
  ThreadManager();
  ~ThreadManager();

#if defined(WEBRTC_POSIX)
  pthread_key_t key_;
#endif

#if defined(WEBRTC_WIN)
  const DWORD key_;
#endif

  // The thread to potentially autowrap.
  const PlatformThreadRef main_thread_ref_;
  RTC_DISALLOW_COPY_AND_ASSIGN(ThreadManager);
};

我们来逐步看下这几个方法:

1,static ThreadManager* Instance()

ThreadManager* ThreadManager::Instance() {
  static ThreadManager* const thread_manager = new ThreadManager();
  return thread_manager;
}

从源码可以看到ThreadManager是通过单例模式,通过静态方法Instance()来获取唯一的实例。

2,构造与设置、获取当前线程(跨平台)

#if defined(WEBRTC_POSIX)
ThreadManager::ThreadManager() : main_thread_ref_(CurrentThreadRef()) {
#if defined(WEBRTC_MAC)
  InitCocoaMultiThreading();
#endif
  pthread_key_create(&key_, nullptr);
}
Thread* ThreadManager::CurrentThread() {
  return static_cast<Thread*>(pthread_getspecific(key_));
}
void ThreadManager::SetCurrentThread(Thread* thread) {
#if RTC_DLOG_IS_ON
  if (CurrentThread() && thread) {
    RTC_DLOG(LS_ERROR) << "SetCurrentThread: Overwriting an existing value?";
  }
#endif  // RTC_DLOG_IS_ON
  pthread_setspecific(key_, thread);
}
#endif

#if defined(WEBRTC_WIN)
ThreadManager::ThreadManager()
    : key_(TlsAlloc()), main_thread_ref_(CurrentThreadRef()) {}

Thread* ThreadManager::CurrentThread() {
  return static_cast<Thread*>(TlsGetValue(key_));
}
void ThreadManager::SetCurrentThread(Thread* thread) {
  RTC_DCHECK(!CurrentThread() || !thread);
  TlsSetValue(key_, thread);
}
#endif

构造与析构函数均声明为private。下边看下ThreadManager在不同平台上的构造方式、设置和获取当前线程的系统差异性:

下边是来自网友ice_ly000的一段精辟的阐述,摘录部分如下:我们可以看到在Windows和类Unix系统中实现进行了区分,WEBRTC_POSIX宏表征该系统是类Unix系统,而WEBRTC_WIN宏表征是Windows系统。虽然实现稍微有些许不同,在MAC下还需要调用InitCocoaMultiThreading()方法来初始化多线程库。但是两个构造函数均初始化了成员key_与main_thread_ref_(我们可以看到WebRTC中的私有成员均以下划线结尾)。其中key是线程管理的关键。

key_的初始化:在Windows平台上,key_被声明为DWORD类型,赋值为TlsAlloc()函数的返回值,TlsAlloc()函数是Windows的系统API,Tls表示的是线程局部存储Thread Local Storage的缩写,其为每个可能的线程分配了一个线程局部变量的槽位,该槽位用来存储WebRTC的Thread线程对象指针。如果不了解相关概念,可以看微软的官方文档。在类Unix系统上,key_被声明pthread_key_t类型,使用方法pthread_key_create(&key_, nullptr);赋值。实质是类Unix系统上的线程局部存储实现,隶属于线程库pthread,因此方法与变量均以pthread开头。总之,在ThreadManager的构造之初,WebRTC就为各个线程所对应的Thread对象制造了一个线程局部变量的槽位,成为多线程管理的关键。

main_thread_ref_的初始化:该成员为PlatformThreadRef类型的对象,赋值为CurrentThreadRef()方法的返回值,如源码所示:在Windows系统下,取值为WinAPI GetCurrentThreadId()返回的当前线程描述符,DWORD类型;在FUCHSIA系统下(该系统是Google新开发的操作系统,像Android还是基于Linux内核属于类Unix范畴,遵循POSIX规范,但FUCHSIA是基于新内核zircon开发的),返回zx_thread_self(),zx_handle_t类型;在类Unix系统下,通过pthread库的pthread_self()返回,pthread_t类型。总之,如前文所述,这部分代码肯定是在主线程中所运行,因此,main_thread_ref_存储了主线程TID在不同平台下的不同表示。
感谢ice_ly000!

3,WrapCurrentThread()与UnwrapCurrentThread()

  // Returns a thread object with its thread_ ivar set
  // to whatever the OS uses to represent the thread.
  // If there already *is* a Thread object corresponding to this thread,
  // this method will return that.  Otherwise it creates a new Thread
  // object whose wrapped() method will return true, and whose
  // handle will, on Win32, be opened with only synchronization privileges -
  // if you need more privilegs, rather than changing this method, please
  // write additional code to adjust the privileges, or call a different
  // factory method of your own devising, because this one gets used in
  // unexpected contexts (like inside browser plugins) and it would be a
  // shame to break it.  It is also conceivable on Win32 that we won't even
  // be able to get synchronization privileges, in which case 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值