目录
概述
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函数)。
全文完。