rtc::Thread类
代码位于:WebRTC\src\rtc_base\thread.h/.cc
rtc::Thread类继承了一个消息队列,可以做抛任务的工作,比较灵活。
class RTC_LOCKABLE Thread : public MessageQueue
{
...
}
rtc::PlatformThread
这个线程是一个平台相关的纯粹的线程,因为像rtc::Thread实际上内部有一个消息队列,外部可以不断地抛入任务,让这个线程“忙”起来。而rtc::PlatformThread是一个原生线程的薄封装。看代码就可以知道。
void PlatformThread::Start() {
RTC_DCHECK(thread_checker_.CalledOnValidThread());
RTC_DCHECK(!thread_) << "Thread already started?";
#if defined(WEBRTC_WIN)
stop_ = false;
// See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION.
// Set the reserved stack stack size to 1M, which is the default on Windows
// and Linux.
thread_ = ::CreateThread(nullptr, 1024 * 1024, &StartThread, this,
STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id_);
RTC_CHECK(thread_) << "CreateThread failed";
RTC_DCHECK(thread_id_);
#else
ThreadAttributes attr;
// Set the stack stack size to 1M.
pthread_attr_setstacksize(&attr, 1024 * 1024);
RTC_CHECK_EQ(0, pthread_create(&thread_, &attr, &StartThread, this));
#endif // defined(WEBRTC_WIN)
}
这个线程只能执行初始化的时候指定的一个固定的任务。没有像rtc::Thread那样支持Post,Send方法。
rtc::Thread::ScopedDisallowBlockingCalls
这个类的名字叫Scoped Disallow Blocking Calls,意思就是不允许阻塞当前调用线程。
有时候为了性能或者其他方面的考虑,在一定范围内不允许写切换线程的代码,可以使用上面的类对象来强制辅助实现。这个类的对象在构造的时候记录了调用线程,在析构的时候会判断当前是否处于构造时候的线程,如果不是的话,就触发assert错误,告知开发者,从而提醒开发者去检查代码。
所以在这个对象所在的生命周期内,用户不可以随意改变线程。
void StatsCollector::GetStats(MediaStreamTrackInterface* track,
StatsReports* reports) {
RTC_DCHECK(pc_->signaling_thread()->IsCurrent());
RTC_DCHECK(reports != NULL);
RTC_DCHECK(reports->empty());
rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls;
if (!track) {
reports->reserve(reports_.size());
for (auto* r : reports_)
reports->push_back(r);
return;
}
StatsReport* report = reports_.Find(StatsReport::NewTypedId(
StatsReport::kStatsReportTypeSession, pc_->session_id()));
if (report)
reports->push_back(report);
report = reports_.Find(
StatsReport::NewTypedId(StatsReport::kStatsReportTypeTrack, track->id()));
if (!report)
return;
reports->push_back(report);
std::string track_id;
for (const auto* r : reports_) {
if (r->type() != StatsReport::kStatsReportTypeSsrc)
continue;
const StatsReport::Value* v =
r->FindValue(StatsReport::kStatsValueNameTrackId);
if (v && v->string_val() == track->id())
reports->push_back(r);
}
}
同步调用
我们跨平台代码中中大量使用了范式:
if( !workThread_->IsCurrent() ){
return workThread_->Invoke<RTCResult>(RTC_FROM_HERE,
rtc::Bind(&LavaRtcEngineImpl::updateVideoEncoderParam, this, params));
}
但是,查看webrtc自己的代码中没有这么用的。
webrtc中判断是否当前线程的较少地出现过的用法是(AsyncInvoker::Flush中):
if (Thread::Current() != thread) {
thread->Invoke<void>(RTC_FROM_HERE,
Bind(&AsyncInvoker::Flush, this, thread, id));
return;
}
而最常见、也出现次数最多的用法是直接调用线程的Invoke(\pc\channel.cc,以及pc\peerconnection.cc中较多):
network_thread_->Invoke<void>(
RTC_FROM_HERE, [this, rtp_transport] { SetRtpTransport(rtp_transport); });
可以看出,WebRTC中基本上假定调用者是知道某个任务位于哪个线程中的。所以不需要每次做判断,要么直接调用函数,要么明确做跨线程调用。
比如在PeerConnection中,专门有一个PeerConnectionFactory保存一组线程对象。
跟进线程的Invoke中看的话,可以发现
template <class ReturnT, class FunctorT>
ReturnT Invoke(const Location& posted_from, FunctorT&& functor) {
FunctorMessageHandler<ReturnT, FunctorT> handler(
std::forward<FunctorT>(functor));
InvokeInternal(posted_from, &handler);
return handler.MoveResult();
}
再看InvokeInternal:
void Thread::InvokeInternal(const Location& posted_from,
MessageHandler* handler) {
TRACE_EVENT2("webrtc", "Thread::Invoke", "src_file_and_line",
posted_from.file_and_line(), "src_func",
posted_from.function_name());
Send(posted_from, handler);
}
再看Send:
void Thread::Send(const Location& posted_from,
MessageHandler* phandler,
uint32_t id,
MessageData* pdata) {
if (IsQuitting())
return;
// Sent messages are sent to the MessageHandler directly, in the context
// of "thread", like Win32 SendMessage. If in the right context,
// call the handler directly.
Message msg;
msg.posted_from = posted_from;
msg.phandler = phandler;
msg.message_id = id;
msg.pdata = pdata;
if (IsCurrent()) {
phandler->OnMessage(&msg);
return;
}
AssertBlockingIsAllowedOnCurrentThread();
......
}
可以看到本身rtc::Thread::Invoke中是判断了IsCurrent()的,而且再多看一点的话,会发现这个函数效果上看其实是一个同步执行的函数,相当于是WebRTC提供了一个强制线程切换并执行任务的功能。
异步调用
WebRTC中有一个AsyncInvoke类(继承自MessageHandler),位于\src\rtc_base\asyncinvoker.h/.cc,提供了异步调用的封装,而且支持了lambda表达式:
template <class ReturnT, class FunctorT>
void AsyncInvoke(const Location& posted_from,
Thread* thread,
FunctorT&& functor,
uint32_t id = 0) {
std::unique_ptr<AsyncClosure> closure(
new FireAndForgetAsyncClosure<FunctorT>(
this, std::forward<FunctorT>(functor)));
DoInvoke(posted_from, thread, std::move(closure), id);
}
template <class ReturnT, class FunctorT>
void AsyncInvokeDelayed(const Location& posted_from,
Thread* thread,
FunctorT&& functor,
uint32_t delay_ms,
uint32_t id = 0) {
std::unique_ptr<AsyncClosure> closure(
new FireAndForgetAsyncClosure<FunctorT>(
this, std::forward<FunctorT>(functor)));
DoInvokeDelayed(posted_from, thread, std::move(closure), delay_ms, id);
}
用法方面,在webrtc中随便搜一下AsyncInvoke可以找到很多例子:
void SetMediaTransportStateCallback(
MediaTransportStateCallback* callback) override {
rtc::CritScope lock(&sink_lock_);
state_callback_ = callback;
invoker_.AsyncInvoke<void>(RTC_FROM_HERE, thread_, [this] {
RTC_DCHECK_RUN_ON(thread_);
OnStateChanged();
});
}
void BaseChannel::OnTransportReadyToSend(bool ready) {
invoker_.AsyncInvoke<void>(RTC_FROM_HERE, worker_thread_,
[=] { media_channel_->OnReadyToSend(ready); });
}
还有一个GuardedAsyncInvoker,可以保证所在调用线程析构的时候,创建的Invoker对象可以被正确设置,而不必担心调用线程对象有dangling指针问题。
思路是,在GuardedAsyncInvoker构造的时候关联了当前的线程,并且一旦调用AsyncInvoke,就会获取一个关键帧,保证调用结束之前,调用线程无法析构(因为会调用该GuardedAsyncInvoker的销毁函数,而这个函数使用了关键帧同步状态,所以caller线程对象的析构必须等待),直到GuardedAsyncInvoker对象顺利将任务抛入(Post)指定线程的消息队列。
概况地说就是,GuardedAsyncInvoker内部使用了AsyncInvoker,并加了同步调用线程的机制。
实际上WebRTC中的rtc::Thread才真正提供了线程的异步调用:Post PostDelayed PostAt