Article directory
The thread design in WebRtc is of great reference value. By learning their implementation, the understanding of threads can be improved to a higher level.
In WebRtc, it is not simply to encapsulate the API related to os thread, but to design the thread in combination with the requirements of function realization.
There are two kinds of thread objects in webrtc:
- rtc::Thread object
rtc::Thread
It is the most important thread type in webrtc. It is deeply integrated with the implementation of the WebRtc library.
- rtc::ProcessThread object
rtc::ProcessThread
The thread type is mainly used to perform some timing tasks.
This article will introduce rtc::Thread and its use in WebRtc .
rtc:Thread
threading model
rtc::Thread
The threading model is as follows:
[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (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
Not only does it generate only one thread, it also contains a queue **(message list)**, it is more appropriate to call it an asynchronous task queue.
Its basic function is to execute tasks in the task queue in threads.
It is driven by events, that is to say, message list
when there is no task ( message
), it will block on the event waiting, and when an event wakes up, it will continue to execute tasks in the queue.
Main members and methods
Class Diagram
rtc::Thread
The core classes and methods listed in the class diagram .
Thread
The class inherits from TaskQueueBase
, and the semantics indicate that it is a task queue.
Core member variables:
MessageList messages_
It is the task queue, which is std::list
defined as follows:
typedef std::list<Message> MessageList
, Message
the class is the abstraction of the task.
PriorityQueue delayed_message_
The delayed task queue is a priority queue.
SocketServer* const ss_
Event driver, as you can see from the name, it can handle network IO events.
Core method:
Its core method is ProcessMessage
the sum Get
method.
ProcessMessage
It is the execution body of the thread, and it is awhile(true)
loop, whichGet
continuously takes out tasks from it and then executes them. The method framework is as follows:
bool Thread::ProcessMessages(int cmsLoop) {
while (true) {
Message msg;
//取出任务
Get(&msg, cmsNext);
...
//执行任务
Dispatch(&msg);
}
}
Get
method, first take out the tasks in the queue, if there is no task, then process the event, if there is no event, then block toWait
the method.
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;
}
}
control class method
bool Start()
void Stop()
void Run()
These three are typical thread control methods.
task class method
async method
It is an asynchronous queue with post
a series of methods for delivering tasks to threads for execution. These post
methods are all asynchronous methods and return immediately.
- Put in a
QueuedTask
type of task.QueuedTask
Methods are encapsulated internally asMessage
types.
void PostTask(std::unique_ptr<webrtc::QueuedTask> task) override;
- Put in a
QueuedTask
type of task to execute after delay ms.
void PostDelayedTask(std::unique_ptr<webrtc::QueuedTask> task,
uint32_t milliseconds) override;
- Post tasks to threads for execution.
virtual void Post(const Location& posted_from,
MessageHandler* phandler,
uint32_t id = 0,
MessageData* pdata = nullptr,
bool time_sensitive = false);
- Post the task to the thread and execute it after delay ms.
virtual void PostDelayed(const Location& posted_from,
int delay_ms,
MessageHandler* phandler,
uint32_t id = 0,
MessageData* pdata = nullptr);
- Post the task to the thread and execute it at the specified time point.
virtual void PostAt(const Location& posted_from,
int64_t run_at_ms,
MessageHandler* phandler,
uint32_t id = 0,
MessageData* pdata = nullptr);
The following two are Post
packages based on the above three methods, which are easier to use. Any type of callable object (function object, lambda, normal method) can be passed in.
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)
Using these **Post**
methods, it is very convenient **rtc::Thread**
to deliver tasks between them, that is to say, they can deliver tasks from one **rtc::Thread**
to another .**rtc:Thread**
synchronization method
send
The method posts a task to the thread queue, and returns after the task is executed.
virtual void Send(const Location& posted_from,
MessageHandler* phandler,
uint32_t id = 0,
MessageData* pdata = nullptr);
The following two Invoke
methods are send
method encapsulations, which can be passed in callable objects. And the execution result of the task can be obtained.
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)
Constructor method
You can see from the above class rtc::Thread
that there are many ways to create an object, you can use its constructor directly, and they all require an event driver ( SocketServer
). You can also use create
system methods, which are simpler than constructors.
static std::unique_ptr<Thread> CreateWithSocketServer();
As mentioned earlier, the thread's task queue is driven by events. This method is to create a thread that can handle IO events.
static std::unique_ptr<Thread> Create();
This method is to create an event-driven thread that does not handle IO events.
static Thread* Current();
Wrap the current thread as an Thread
object. Note that if Thread
the object is generated by calling this method, the method cannot be called again Start
. Because the thread is already running, there is no need to call it again. Its queue can be utilized by using methods like this:
Thread* th_main = Thread::Current();
th_main->ProcessMessages(kForever);
The purpose of rtc::Thread
**rtc::Thread**
It serves three purposes:
- Used as an ordinary package class (requires overloading the Run() method).
**Thread**
- Used as a message queue (the SocketServer object created at this time is NullSocketServer, and the role of NullSocketServer is to make the thread event-driven instead of poll-driven).
- It is used as a message queue with the function of managing IO (the SocketServer object created at this time is PhysicalSocketServer, which can handle I/O events).
Three major threads in WebRtc
The threading model in WebRtc is very clear. It is divided into three major threads according to their functions, each of which performs its own duties.
- Signaling thread
**signaling_thread**
, the thread that calls the WebRtc API (that is, the client program thread). - Worker thread
**worker_thread**
, the thread for internal routine logic work. - Network thread
**network_thread**
, data sending and receiving thread.
They are all rtc::Thread
objects. When creating a PreeConnection object, it is required to pass in these three thread objects.
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);
Thread pointer, NULL can be passed in, created internally by WebRtc.
network_thread is a SocketServer
owned_network_thread_ = rtc::Thread::CreateWithSocketServer();
owned_network_thread_->SetName("pc_network_thread", nullptr);
network_thread, as the name suggests, is used to send and receive packets. The following is the receiving stack of network_thread.
network_queue packet sending stack
worker_thread is a message queue
owned_worker_thread_ = rtc::Thread::Create(); //创建的是NullSocketServer
owned_worker_thread_->SetName("pc_worker_thread", nullptr);
signaling_thread is also a message queue
Inside WebRtc, if signaling_thread
it is NULL, the thread calling the API will be wrapped into one rtc::thread
.
The post method used by the three major threads **rtc::Thread**
delivers tasks to each other and performs thread switching, and the division of responsibilities is very clear.
rtc::Thread
usage of
Create a without network IO functionrtc::Thread
- Defines a task, which is a function object.
//一个任务
struct ThreadTask
{
void operator()()
{
std::cout<<std::this_thread::get_id()<<std::endl;
}
};
- create thread
auto p1 = Thread::Create();
p1->Start();
p1
send one toThreadTask
p1->PostTask(RTC_FROM_HERE,ThreadTest());
p1
Post a deferred execution toThreadTask
//延迟1秒执行
p1->PostDelayedTask(RTC_FROM_HERE,ThreadTest(),1000);
Create an IO functionrtc::Thread
std::unique_ptr<Thread> th1 = Thread::CreateWithSocketServer()
rtc::Thread
example in unit test
To learn rtc::Thread
more about how to use it, you can take a look at its unit test code. The following is PostTask
an example of the interface, and you can PostTask
pass in various callable objects.
InvokesWitBind
example
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
example
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
example
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
example
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
example
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);
}