【WebRTC】中的线程

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值