WebRTC源代码探索之旅——多线程篇(4 - 1)

4 messagequeue

 

messagequeue.h/messagequeue.cc文件是多路信号分离器的重要组成部分。它实现了消息一个完整地消息队列,该队列包括立即执行消息队列、延迟执行消息队列和具有优先级的消息队列。其中,talk_base::MessageQueue类也是talk_base::Thread类的基类。所以,所有的WebRTC的线程都是支持消息队列的。

 

4.1 talk_base::MessageQueueManager

 

talk_base::MessageQueueManager类是一个全局单例类。这个类看似比较复杂,但是功能其实非常简单——仅仅为了在所有的talk_base::MessagerQueue中删除与指定的talk_base::MessageHandler相关的消息。WebRTC的消息队列在发送消息的时候要指定消息处理器(talk_base::MessageHandler)。如果某个消息处理器被析构,那么与之相关的所有消息都将无法处理。所以,创建了这个全局单例类来解决这个问题(见talk_base::MessageHandler析构函数)。

 

talk_base::MessageQueueManager的代码没有涉及任何跨平台的API调用,而且本身功能也非常简单。所以我就不讨论它如何使用std::vector管理talk_base::MessageQueue。唯一需要注意的就是talk_base::MessageQueueManager如何保证自己在第一个talk_base::Thread类实例化之前完成talk_base::MessageQueueManager全局单例的实例化。这当中有个有趣的状况,talk_base::MessageQueueManager在保证了自己必然在第一条子线程被创建之前自己被实例化,talk_base::MessageQueueManager:: Instance函数内部没有使用任何锁来保护talk_base::MessageQueueManager::instance_实例的创建。

 

如前面所说,talk_base::MessagerQueue是talk_base::Thread的基类。在创建talk_base::Thread时必然会调用talk_base::MessagerQueue的构造函数。在talk_base::MessagerQueue的构造函数中调用了talk_base::MessageQueueManager::Add函数,而该函数会使用talk_base::MessageQueueManager::Instance函数创建talk_base::MessagerQueueManager的实例。由于talk_base::ThreadManager保证了在创建第一个子线程之前,主线程会被包装成talk_base::Thread对象,所以talk_base::MessageQueueManager必然可以将主线程作为第一个talk_base::MessageQueue对象纳入管理。

 

以上的描述可能比较晦涩难懂,这是因为整个流程涉及到了talk_base::Thread和talk_base::ThreadManager等类。而这些都是尚未讲解过他们的代码。不过即使看不明白也没关系,我会在讲解完所有相关类之后演示2段范例代码,并将范例代码的调用栈完全展开。看过范例代码后绝大多数读者都应该能够明白talk_base::MessageQueueManager的原理。

 

talk_base::MessageQueueManager还有最后一个问题,那就是它什么时候被析构。talk_base::MessageQueue的析构函数会调用talk_base::MessageQueueManager::Remove函数,并且“理论上来说”在最后一个talk_base::MessageQueue从队列中移除之后会析构talk_base::MessageQueueManager。既然,所有的线程都被移除,那就意味着talk_base::MessageQueueManager实例在被delete时重新回到了单线程的环境,所以也没有任何锁的保护。

 

4.2 MessageData

 

这一节的内容将包括talk_base::MessageData类以及多个它的子类和几个工具函数。这些类和函数都很简单,所以就不介绍代码和原理,仅仅罗列一下它们的功能。

 

4.2.1 talk_base::MessageData

 

定义了基类,并将析构函数定义为虚函数。

 

4.2.2 talk_base::TypedMessageData

 

使用模板定义的talk_base::MessageData的一个子类,便于扩展。

 

4.2.3 talk_base::ScopedMessageData

 

类似于talk_base::TypedMessageData,用于指针类型。在析构函数中,自动对该指针调用delete。

 

4.2.4 talk_base::ScopedRefMessageData

 

类似于talk_base::TypedMessageData,用于引用计数的指针类型。

 

4.2.5 talk_base::WrapMessageData函数

 

模板函数,便于创建talk_base::TypedMessageData

 

4.2.6 talk_base::UseMessageData函数

 

模板函数,用于将talk_base::TypedMessageData中的Data取出

 

4.2.7 talk_base::DisposeData

 

这是一个很特殊的消息,用以将某个对象交给消息引擎销毁。可能的用途有2个:1. 有些函数不便在当前函数范围内销毁对象,见范例talk_base::HttpServer::Connection::~Connection;2.某对象属于某一线程,因此销毁操作应该交给所有者线程(未见范例)。WebRTC用户不需要自行使用该类,调用talk_base::MessageQueue::Dispose函数即可使用它的功能。

 

以上7个类或函数的实现非常简单,有C++使用经验的读者非常容易就能理解(标准库中就有相似的类)。


4.3 Message

 

这一节将简单介绍一下3个类:talk_base::Message、talk_base::DelayedMessage和talk_base::MessageList。

 

4.3.1 talk_base::Message

 

定义了消息的基本数据结构。

 

4.3.2 talk_base::DelayedMessage

 

定义了延迟触发消息的数据结构。在talk_base::MessageQueue中,延迟消息被存放在以talk_base::DelayedMessage::msTrigger_排序(talk_base::DelayedMessage类定义了operator<)的队列中。如果2个延迟消息的触发时间相同,响应顺序按先进先出原则。

 

这里我将简单介绍一下各个成员变量的用途:

cmsDelay_:延迟多久触发消息,仅作调试使用

msTrigger_:触发消息的时间

num_:添加消息的时间

msg_:消息本身

 

在使用延迟消息时,不需要自行构建talk_base::DelayedMessage实例。直接调用talk_base::MessageQueue::PostDelayed或者talk_base::MessageQueue::PostAt函数即可。

 

4.3.3 talk_base::MessageList

 

消息列表,定义为std::list

你可能感兴趣的:(webrtc)