5.9.Webrtc线程事件处理

在前面的课程中呢,我已经向你介绍了事件处理的一些基础知识,那今天呢,我们再来看一下外边儿rtc下事件处理的基本逻辑是什么?

那首先呢,我们来看一下事件是如何协调线程工作的,那就如果这张图所展示的有两个线程,对吧?一个是限定线程,一个是工作线程。其中,工作线程呢,

又称为等待线程,它会因为一个事件而睡眠,直到事件发生为止。这两个线程是如何协调工作的呢?那中间儿就要有一个公共对象在外边儿tc下,这个公共对象就是non socket server或者是physical socket server。这在我们上节课中都向你做过介绍了,对吧?

那对于工作线程来说,它要等待事件就是等待。公共对象中的某个事件,那当等待这个事件的时候呢?整个线程就处于睡眠状态。也就不工作了。而对于信令线程或者说发送事件的线程来说呢,

它会主动发送一个事件给公共对象,那当公共对象收到这个事件之后呢,就会将睡眠的线程。唤醒这样呢,这个睡眠的线程就可以继续工作了,这是事件协调线程工作的一个基本的原理OK?

那下面呢,我们再来看看web rtc下是如何这个控制事件的发生的,那对于web rtc来说,它有两个。事件的发生源那第一个呢是信令线程对吧?那信令线程可以通过主动触发事件。来调用公共对象,从而呢,唤醒等待线程,

这是第一个触发源,那第二个触发源呢,一般看不到。它是由系统层触发的,也就是通过socket事件来触发公共对象,从而将等待线程唤起。实际上呢,这块知识点在我们前面的课程中呢,也大概向你做了一点介绍。那这张图呢,可以使你更加清楚的了解这两个事件源好,那了解了上面这些知识之后呢,下面我们就来看一下,对于等待线程,它的处理逻辑是怎样的?

实际这张PPT呢,我们在之前也向你做过介绍,那对于等待线程来说,它处理逻辑非常简单,就是通过一个死循环。不断的执行,那在这个执行过程中呢,主要做两件事儿,第一件事儿呢是通过get从队列中获取消息。如果此时队列中有消息,那它就会将这个消息交给dispatch,通过dispatch函数对这个消息进行处理。那如果队列中没有消息,在get函数内部会进行等待,也就是说指当前线程处于睡眠状态,

等待时间的触发。这个逻辑呢,非常的清晰,那接下来呢,我们来看一下get函数中做了什么事,

那对于get函数来说呢,它有两层循环,我们来看一下。它里边逻辑是怎样的?首先我们来看看,在这个大的循环里边儿,这个小的循环,它的作用是什么?那这里呢?我们第一个看到的是获得锁,

对吧?那获得这个锁的含义是什么呢?实际这个锁就是队列锁。当两个线程对同一个队列进行操作的时候,那都需要先获得这把锁之后才能对队列进行操作。对于发送消息的线程来说,它首先要获得这把锁,之后呢,才能向队列中插入消息。而对于获取数据的线程来说呢,它也要先获得这把锁之后呢,才能从堆列中取出消息。当获得这把锁之后,我们来看get函数做了哪些事啊?它首先判断这个消息队列是否为空。

那如果为空,它直接就退出这个while循环了,对吧?那退出这个循环之后,它做哪件事呢?就是调socket server的weight函数。那在weight函数中呢,就会调用事件等待API,等待事件的触发。那如果队列不为空,它就会从队列中取出这个消息,并且呢,将消息弹出队列,并从这个函数返回。那由于p message是输出参数,

所以通过它的输出参数呢?那外层就可以拿到这个消息进行进一步的处理了,也就是交由dispatch。进行消息的处理,那所以这个逻辑啊,还是非常简单的。也就是说,在大的循环里头,包括了两块儿,一块儿呢,是对队列的操作,那当队列为空的时候呢,它就进入睡眠状态。等待信号的触发,那如果有信号过来之后。

它就会继续执行,对吧?那对于wait来说,它可以设置成永久等待,也可以设置成短时等待。如果是短时等待,当超时之后。它又通过这个外部循环进入了睡眠状态,这就是外层循环,它的作用,那内层循环呢?适用于处理消息,外层循环呢?适用于处理当wait超时之后继续等待。OK,

那这是get函数,那这里呢?我们还要关注一下wait函数,看看wait函数里是怎么实现的?对于位的函数来说呢,不同的socket server,它的时间是不一样的,那有non socket server和physical socket server,那这里呢,我以physical socket server为例。来看一下位的函数的实现,我们只要了解了physical socket server的实现,就可以知道non socket server它是怎么做的了。好,对于physical socket server的weight函数来说呢,

它又一层循环,对吧?在这个循环里头。有一个for循环,这个for循环呢?是用于我们将socket与事件进行绑定的,也就是调用的wsa even的select这个API。对吧,这个API呢,我们在前面已经向你做过详细介绍了,那这个API不清楚的同学可以再翻一下,上面我们讲解的内容。当所有的socket都与事件绑定之后。它会调用wsa wait for multiple events,这个API那这个API实际就是对事件进行检查。

如果事件来了,它就继续执行下面的操作,如果没有事件,它就会让这个线程呢,直接在这里。进行睡眠对吧?直到事件发生为止。那我们来看一下这个函数,它几个参数,实际这几个参数呢,我们前面也做过介绍,那第一个参数呢,就是我们这个事件数组中。一共有多少个事件对吧?第二个呢?

是事件的数组,第三个是是否所有的事件都触发才唤醒这个线程?那FALSE呢,就是只要有一个时间触发了。它就唤醒了,对吧?好,再下一个参数负一就是永远等待,直到有事件发生,这个参数呢?就是time out最后一个参数呢?我们设成了。好,那再接下来啊,如果有时间触发了对于physical socket server的weight函数来说。

它要判断是哪个socket触发的事件,这个时候呢,在这个for循环中就会调用ws a。enumerate network event这个API那在这个API中呢?它会对这个事件中的所有socket进行遍历。如果他发现输出参数evs的network events,某个事件被置位了,那他就进行相应的逻辑处理。那否则呢,如果没有任何事件,它在循环遍历下一个socket,那这就是physical socket server。它的一个主要逻辑。当对于web rtc的weight实现来说呢,它里边包含了很多代码,(这就是一个很正常的select的处理流程)

这里呢,是我将一些不重要的代码简化之后,剩下的主干代码。那通过这个主干代码,我们就可以知道它真正做了什么事了,对吧?好,那现在呢?我们就将等待线程的整个逻辑向你做了介绍,首先通过get获取消息。对吧,在get内部呢,它会进行判断是否队列为空,如果队列为空或者是没有触发socket事件。那它就会调用wait函数,

最终调用wait for multiple events,这个API使线程处于睡眠状态。那当有事件来了,或者是队列不为空了,或者是发生骚位的事件了,那这个时候呢,他会从睡眠状态唤起。那唤起之后呢,就做后边的这段逻辑对吧?对这个三维的事件进行处理,或者是从队列中取出消息。一个是s时间,延安在这整个过程,我们了解之后呢,下面我们就来看一下外边tc源码,

咱们过一下这个逻辑。好,我们切换到Windows系统下,那在这里呢?首先我们要打一些断点,在哪里打呢?实际主要就是在three的点CC这个文件中,那这个文件是在哪个目录下呢?是在rtc base下边儿的rtc base,这个工程在这个工程中呢,就包含了three的点儿CC文件。好在这个文件中呢,首先我们可以搜一下。process.那实际上这个函数我们在前面的课程中也向你做过介绍,

对吧?当我们找到这个process之后呢,我们看一下它的实现。这就是这里。在984行。这样呢,我们就在process message这个函数中呢,设一个断点在哪设呢?就是在993行。在这个while循环这儿,我们设一个断点,这样我们就可以看到等待线程,它的处理逻辑是怎样的了,对吧?当我们将这个断点设置好之后呢?

我们将peer connection clan端给启动起来。继续执行。连接新的服务器。那这块我们先不管,先继续执行。我们连接一个对端。那首先呢,它会创建peer connection factory对吧?这个我们在之前都讲过,那在创建peer connection factory的时候呢?会创建几个线程?一是网络线程,二是工作线程。那当线程启动起来之后呢?这些线程呢?

就开始工作了,对于等待线程来说,那它就会跳到while循环上去。处理消息,我们继续执行好,这是它内部去创建physical socket server,对吧?在创建。physical socket server的时候呢?在它里边,会创建一个事件hev。这个事件是用于处理消息队列的。

那还会创建另外一个事件是socket ev这个事件呢,是用于处理socket消息的。

所以它一共有两个事件源,这我们都看到了,对吧?分别是。消息队列。还有socket好,我们继续执行。

那通过上面这段代码呢,我们就将网络线程创建好了,接下来呢,它创建工作线程对于工作线程来说呢,它使用的是n。设位的设备。好,我们进入到这个函数中。那在这个函数中呢?

684行,我们可以看到。对于这个线程,它使用的是non socket server,对吧?我们继续往下走。好,这时候呢,我们就将工作线程也启动起来了,那当工作线程启动的时候呢,我们可以看到之前创建的网络线程已经处于。等待状态了,我们来看一下它的调用站。那从这个调用站中呢,我们就可以知道是网络编程处于了等待状态,

那首先呢,我们来看一下process message(等待线程或者说是网络线程)。那在这个函数中呢?在998行,它调用了get函数对吧?那在get函数中,首先有一个well循环。也就是442行在这个while循环的下边,又有一个while循环446行在446行这个while循环里边。它首先获得队列锁对吧?451行那拿到这个锁之后呢?它会做一堆逻辑,那这一块逻辑呢?我们暂时先不管,因为它不是核心逻辑。

那核心逻辑是哪块呢?是467行到472行。也就是首先判断队列是否为空,如果为空就直接退出了,退出之后它就进入睡眠状态。否则的话呢,它就从队列中取出一个消息,取出这个消息,它干什么呢?取出这个消息之后,它就直接返回了,对吧?那回到上一层,就会对获取到消息进行处理。那如果没有消息,

它会怎么办呢?它就会调用位的函数进行等待,对吧?好,那下面呢?我们再来看看wait函数做了哪些事情?那wait函数呢?准备执行wait for multiple events这个API了。那对于这个API来说呢,它的输入参数与我们在PPT中写的是不一样的,但是它的含义是类似的。也是事件的个数,事件数组这个为就是当有事件来了之后就立马触发。不需要等待所有的事件都来。等待超时时间最后一是那也是类似的,

那我们来看看位的函数主要都做了哪些事儿?那在weight函数的一开始呢,在这个循环中,首先它要构造一些事件,对吧?都有哪些事件进行侦听?那么,需要把要侦听的这些事件呢?塞到这个事件数组中。

之后呢,它会处理所有的socket。从每一个dispatcher中拿到它的socket的描述符。那之后呢,通过event select将这个socket与事件进行一个绑定,对吧?

之后呢?当有事件发生的时候,我们就可以知道是哪个socket触发的事件。好,如果我们拿到的这个socket,它不是一个真正的socket。那有可能是一个普通事件,对吧?那我们就把它当做一个普通事件,塞到事件处理函数中之后呢?就是调用wait for multiple event这个API。进行事件等待。当有线程触发了事件之后呢,它就会对返回值做一些判断,如果失败了怎么样?

否则成功了,它会根据返回值计算出发生事件的索引,最终呢,从事件。owner数组中取出对应的dispatcher。那么,拿到第四拍摄之后,进行相应的处理。另外呢,返回的index也是有两层含义。那一种呢,是普通的事件的index,另外一种呢,是socket index,那对于socket来说。

它会进入到socket处理逻辑中,就会遍历所有的dispatcher,那看看是不是对应的socket触发了事件。

如果是的话,就会进行socket枚举,最终呢,找到是谁触发的事件,从而处理相应的socket,对吧?这就是。这段逻辑,它的一个主要工作。OK,那下面呢?我们就来看一下是谁触发的事件呢?

你继续执行。在397行的wake up socket server就是触发事件的API。那在它内部呢,会调用socket server的wake up函数,从而呢,将等待线程换解,那下面呢,我们就通过这个调用站来看一下到底是谁?触发的这个事件。这个调用站啊,是从上到下看,也就是说最顶上的是我们最近调用的函数,最底下的呢是它的源就是谁调用的?那首先我们将调用站往下拉动,我们可以看到一个非常熟悉的身影,

就是create modular peer connection factory这个API。也就显然,这个事件呢,是我们在调用peer connection对象的时候触发的,对吧?在它内部呢,是做了多层的调用,那这些调用呢,我会在后边向你做介绍,那么我们再往上找找。找谁呢?找我们比较熟悉的API在这一块儿,我们会看到。当peer connection factory创建成功之后呢,它会调用initialize来对这个对象进行初始化。

初始化的时候又会创建China manager,那manager创建好之后呢,又会调用它的nit。那我们看到这个标记之后呢?往上找。你就会发现在113行有一个invoke函数。

那113行的含义就是向网络线程发送一个消息,那这个消息呢?就是由我们括号中的这段代码来组成的。对,也就是说,让网络线程来执行这段代码,因为是在创建peer connection factory对象,所以它一定是在信令线程。所以是信令线程调用了invoke向网络线程设置的一个任务。

那这个任务是什么呢?就是这个匿名函数。那这个任务就会被插入到网络线程的队列中,那我们继续执行啊。那任务插入成功之后呢,就会触发一个事件,那这个事件呢,就是由wake up socket server来触发的,那在这个函数中呢,又会调用。socket server的wake up函数。在wake up函数中,会调用signal wake up的signal方法。好在,signal方法中呢?

会调用。wsa set event,那像physical socket server触发一个普通事件这样呢,它又开始执行1857行以后的代码了。

那以上呢,就是web rtc线程事件的处理逻辑OK,那通过上面的讲解呢,你应该对。y8 rtc的事件处理逻辑非常清楚了,对吧?知道了,等待线程它都做了哪些事情?发送线程又做了哪些事情?那关于我们在代码调试过程中出现的invoke sent post等这一类函数呢?我们在后边的课程中再向你做详细介绍。

那这些呢,涉及到线程的切换,以及像其他线程队列中插入消息等,这一系列操作,那这一些呢,我们都会向你做详细介绍。那我们今天的课呢,就到这里有任何的问题呢,您可以到讨论区或者是群里去给我留言,我在那里呢,给你做详细解答好,谢谢。

相关推荐
Be_Somebody1 分钟前
[这可能是最好的Spring教程!]Maven的模块管理——如何拆分大项目并且用parent继承保证代码的简介性
java·spring boot·spring·spring入门
计算机学姐10 分钟前
基于Python的高校成绩分析管理系统
开发语言·vue.js·后端·python·mysql·pycharm·django
VertexGeek12 分钟前
Rust学习(三):rust基础Ⅱ
开发语言·学习·rust
一个数据小开发17 分钟前
业务开发问题之ConcurrentHashMap
java·开发语言·高并发·map
会飞的架狗师33 分钟前
【Spring】Spring框架中有有哪些常见的设计模式
java·spring·设计模式
Jakarta EE44 分钟前
在JPA和EJB中用乐观锁解决并发问题
java
三小尛44 分钟前
插入排序(C语言)
c语言·开发语言
南宫理的日知录1 小时前
106、Python并发编程:深入浅出理解线程池的内部实现原理
开发语言·python·学习·编程学习
花心蝴蝶.1 小时前
并发编程中常见的锁策略
java·jvm·windows
WolvenSec1 小时前
C/C++逆向:结构体逆向分析
c语言·开发语言·c++·网络安全