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等这一类函数呢?我们在后边的课程中再向你做详细介绍。

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

相关推荐
Scott9999HH3 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔3 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社4 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海4 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖5 小时前
JDK 26 新特性详解
java·开发语言
马优晨6 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区7 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大8 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai8 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说8 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json