webrtc中的线程(一) rtc::Thread


WebRtc中的线程设计非常具有参考价值,通过学习它们的实现,可以对线程的理解更上一个台阶。
在WebRtc并不是简单的对os 线程相关的API进行封装,而是结合功能实现需求对线程进行了设计。
在webrtc有两种线程对象:

  1. rtc::Thread 对象

rtc::Thread是webrtc中最重要的线程类型。它与WebRtc库的实现深度结合。

  1. rtc::ProcessThread 对象

rtc::ProcessThread线程类型,主要是用作执行一些定时任务。

这篇文章将介绍rtc::Thread及它在WebRtc中的使用

rtc:Thread

线程模型

rtc::Thread的线程模型如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GZPu3OiZ-1678665809251)(https://note.youdao.com/yws/api/personal/file/WEBff381d87c74c6937c35f05c025579f26?method=download&shareKey=d2fd984d0ab7f17fd5395027c1aa3fab#clientId=ua4f980d8-c6d9-4&id=nodDw&originHeight=1190&originWidth=1574&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u1c3d1033-3e4f-4638-8ea8-2f6a65535ea&title=)]
如上图所示,rtc::Thread不仅仅是只产生一个线程,它还包含一个队列**(message list)**,将它称为异步任务队列更适合。

它的基本功能就是在线程中执行任务队列中的任务。

它靠事件驱动,也就是说当message list中没有任务(message)时,它会阻塞在事件等待上,当有事件唤醒时后会继续在队列中去任务执行。

主要成员及方法

类图

类图中列出了rtc::Thread的核心类及方法。
rtc_thread_1.png

Thread类继承自TaskQueueBase,从语意就表明了它是一个任务队列。

核心成员变量:

  • MessageList messages_

就是任务队列,它是一个std::list,定义如下:
typedef std::list<Message> MessageListMessage类就任务的抽象。

  • PriorityQueue delayed_message_

延迟任务队列,是一个优先队列。

  • SocketServer* const ss_

事件驱动器,从名字上就可以看出它可以处理网络IO事件。

核心方法:

它的核心方法就是ProcessMessageGet方法。

  • ProcessMessage是线程的执行体,是一个while(true)循环,它不断的从Get中取出任务然后执行,方法框架如下:
bool Thread::ProcessMessages(int cmsLoop) {
    
    
	while (true) {
    
    
        Message msg;
        //取出任务
        Get(&msg, cmsNext);
        ...
        //执行任务
        Dispatch(&msg);
    }
}
  • Get方法,首先取出队列中的任务,如果没有任务则处理事件,如果没有事件则阻塞到Wait方法上。
bool Thread::Get(Message* pmsg, int cmsWait, bool process_io) {
    
    
    //取出队列中的任务
	while(true) {
    
    
        ...
        if (messages_.empty()) {
    
    
          break;
        } else {
    
    
          *pmsg = messages_.front();
          messages_.pop_front();
        }
        ...
    	return true;
    }

    ...
    
	//如果没有任务则执行事件,如何也没有事件就阻塞在Wait方法上
    {
    
    
      // Wait and multiplex in the meantime
      if (!ss_->Wait(static_cast<int>(cmsNext), process_io))
        return false;
    }
}
控制类方法

bool Start()
void Stop()
void Run()
这三个是典型的线程控制方法。

任务类方法

异步方法

它是异步队列,有post系列方法,用于将任务投递到线程中执行,这些post方法都是异步方法,马上返回。

  • 放入一个QueuedTask类型的任务。QueuedTask方法内部被封装成了Message类型。
void PostTask(std::unique_ptr<webrtc::QueuedTask> task) override;
  • 放入一个QueuedTask类型的任务,在delay ms后执行。
void PostDelayedTask(std::unique_ptr<webrtc::QueuedTask> task,
                       uint32_t milliseconds) override;
  • 将任务投递到线程中执行。
virtual void Post(const Location& posted_from,
                    MessageHandler* phandler,
                    uint32_t id = 0,
                    MessageData* pdata = nullptr,
                    bool time_sensitive = false);
  • 将任务投递到线程中,在delay ms后执行。
virtual void PostDelayed(const Location& posted_from,
                           int delay_ms,
                           MessageHandler* phandler,
                           uint32_t id = 0,
                           MessageData* pdata = nullptr);
  • 将任务投递到线程中,在指定时间点执行。
virtual void PostAt(const Location& posted_from,
                      int64_t run_at_ms,
                      MessageHandler* phandler,
                      uint32_t id = 0,
                      MessageData* pdata = nullptr);

下面两个是基于上面三个Post方法的封装,它们更易用。可传入任意类型的可调用对象(函数对象,lambda,普通方法)。

template <class FunctorT>
  void PostTask(const Location& posted_from, FunctorT&& functor);
template <class FunctorT>
  void PostDelayedTask(const Location& posted_from,
                       FunctorT&& functor,
                       uint32_t milliseconds)

利用这些**Post**方法可以很方便的在**rtc::Thread**间进行任务投递,也就是说可以在一个**rtc::Thread**中向另外一个**rtc:Thread**投递任务。

同步方法

send方法向线程队列中投递一个任务,等任务执行完后才返回。

virtual void Send(const Location& posted_from,
                    MessageHandler* phandler,
                    uint32_t id = 0,
                    MessageData* pdata = nullptr);

下面两个Invoke方法是对send方法的封装,可以传入可调用对象。并且可以获得任务的执行结果。

template <
      class ReturnT,
      typename = typename std::enable_if<!std::is_void<ReturnT>::value>::type>
  ReturnT Invoke(const Location& posted_from, FunctionView<ReturnT()> functor);
template <
      class ReturnT,
      typename = typename std::enable_if<std::is_void<ReturnT>::value>::type>
  void Invoke(const Location& posted_from, FunctionView<void()> functor)

构造类方法

从上面的类可以看到创建rtc::Thread对象有多种方式,可以直接使用它的构造函数,它们都要求传入一个事件驱动器(SocketServer)。也可以使用create系统方法,相比构造函数更简单些。

  • static std::unique_ptr<Thread> CreateWithSocketServer();

前面提到thread的任务队列是靠事件驱动,这个方法就是创建一个可以处理IO事件的thread。

  • static std::unique_ptr<Thread> Create();

该方法是创建一个不处理IO事件,靠事件驱动的thread。

  • static Thread* Current();

将当前线程包装为一个Thread对象,注意如果调用该方法产生Thread对象,后不能再调用Start方法。因为线程已经运行了,无需再调用。可以像如下使用方法,来利用它的队列:

Thread* th_main = Thread::Current();
th_main->ProcessMessages(kForever);

rtc::Thread的用途

**rtc::Thread**它有三个用途:

  1. 作为一个普通的**Thread**封装类来使用(需要重载Run()方法)。
  2. 作为一个消息队列来使用(此时创建的SocketServer对象是NullSocketServer,NullSocketServer的作用是让线程为事件驱动,而不是轮询驱动)。
  3. 作为一个具有管理IO功能的消息队列来使用(此时创建的SocketServer对象是PhysicalSocketServer,可以处理I/O事件)。

WebRtc中的三大线程

WebRtc中的线程模型非常清晰,按功能划分为三大线程,各司其责。

  • 信令线程 **signaling_thread**,调用WebRtc API的线程(就是客户程序线程)。
  • 工作线程 **worker_thread**,内部常规逻辑工作的线程。
  • 网络线程**network_thread**,数据收发线程。

它们都是rtc::Thread对象。创建PreeConnection对象时,就要求传入这三个线程对象。

RTC_EXPORT rtc::scoped_refptr<PeerConnectionFactoryInterface>
CreatePeerConnectionFactory(
rtc::Thread* network_thread,
rtc::Thread* worker_thread,
rtc::Thread* signaling_thread,
rtc::scoped_refptr<AudioDeviceModule> default_adm,
rtc::scoped_refptr<AudioEncoderFactory> audio_encoder_factory,
rtc::scoped_refptr<AudioDecoderFactory> audio_decoder_factory,
std::unique_ptr<VideoEncoderFactory> video_encoder_factory,
std::unique_ptr<VideoDecoderFactory> video_decoder_factory,
rtc::scoped_refptr<AudioMixer> audio_mixer,
rtc::scoped_refptr<AudioProcessing> audio_processing);

线程指针,可以传入NULL,由WebRtc内部创建。

network_thread 是一个SocketServer
owned_network_thread_ = rtc::Thread::CreateWithSocketServer();
owned_network_thread_->SetName("pc_network_thread", nullptr);

network_thread顾名思义,用来收发包。如下是network_thread的收包堆栈。
network线程收流流程.png

network_queue发包堆栈
image.png

worker_thread 是一个消息队列
owned_worker_thread_ = rtc::Thread::Create(); //创建的是NullSocketServer
owned_worker_thread_->SetName("pc_worker_thread", nullptr);
signaling_thread 也是一个消息队列

在WebRtc内部,如果signaling_thread为NULL,会将调用API的线程包装成一个rtc::thread

三大线程利用**rtc::Thread**的post方法,相互投递任务,进行线程切换,责任划分就非常清晰。

rtc::Thread的用法

创建一个没有网络IO功能的rtc::Thread

  • 定义一个任务,它是一个函数对象。
//一个任务
struct ThreadTask
{
    
    
    void operator()()
    {
    
    
        std::cout<<std::this_thread::get_id()<<std::endl;
    }
};
  • 创建线程
auto p1 = Thread::Create();
p1->Start();
  • p1投递一个ThreadTask
p1->PostTask(RTC_FROM_HERE,ThreadTest());
  • p1投递一个延迟执行的ThreadTask
//延迟1秒执行
p1->PostDelayedTask(RTC_FROM_HERE,ThreadTest(),1000);
创建一个有IO功能的rtc::Thread
std::unique_ptr<Thread> th1 = Thread::CreateWithSocketServer()

rtc::Thread单元测试中例子

了解rtc::Thread更多的使用方法,可以看看它的单元测试代码,下面列举PostTask接口的例子,可以向PostTask中传入各式可调用对象。

  • InvokesWitBind示例
TEST(ThreadPostTaskTest, InvokesWithBind) {
    
    
    std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
    background_thread->Start();

    Event event;
    background_thread->PostTask(RTC_FROM_HERE, Bind(&Event::Set, &event));
    event.Wait(Event::kForever);
}
  • InvokesWithLambda示例
TEST(ThreadPostTaskTest, InvokesWithLambda) {
    
    
    std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
    background_thread->Start();

    Event event;
    background_thread->PostTask(RTC_FROM_HERE, [&event] {
    
     event.Set(); });
    event.Wait(Event::kForever);
}
  • InvokesWithCopiedFunctor示例
TEST(ThreadPostTaskTest, InvokesWithCopiedFunctor) {
    
    
    std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
    background_thread->Start();

    LifeCycleFunctor::Stats stats;
    Event event;
    //LifeCycleFunctor是有拷贝语义的对象
    LifeCycleFunctor functor(&stats, &event);
    background_thread->PostTask(RTC_FROM_HERE, functor);
    event.Wait(Event::kForever);

    EXPECT_EQ(1u, stats.copy_count);
    EXPECT_EQ(0u, stats.move_count);
}
  • InvokesWithMovedFunctor示例
TEST(ThreadPostTaskTest, InvokesWithMovedFunctor) {
    
    
    std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
    background_thread->Start();

    LifeCycleFunctor::Stats stats;
    Event event;
    LifeCycleFunctor functor(&stats, &event);
    //通过std::move产生右值引用
    background_thread->PostTask(RTC_FROM_HERE, std::move(functor));
    event.Wait(Event::kForever);

    EXPECT_EQ(0u, stats.copy_count);
    EXPECT_EQ(1u, stats.move_count);
}
  • InvokesWithReferencedFunctorShouldCopy示例
TEST(ThreadPostTaskTest, InvokesWithReferencedFunctorShouldCopy) {
    
    
    std::unique_ptr<rtc::Thread> background_thread(rtc::Thread::Create());
    background_thread->Start();

    LifeCycleFunctor::Stats stats;
    Event event;
    LifeCycleFunctor functor(&stats, &event);
    //传入对象引用
    LifeCycleFunctor& functor_ref = functor;
    background_thread->PostTask(RTC_FROM_HERE, functor_ref);
    event.Wait(Event::kForever);

    EXPECT_EQ(1u, stats.copy_count);
    EXPECT_EQ(0u, stats.move_count);
}

猜你喜欢

转载自blog.csdn.net/mo4776/article/details/129329974
RTC