5.7.webrtc线程的启动与运行

那在上一节课中呢?我向你介绍了web rtc的三大线程,包括了信令线程,工作线程以及网络线程。那同时呢,我们知道了web rtc 3大线程创建的位置以及运行的时机。

对吧,那么今天呢?我们再继续深入了解一下,看这些线程是如何创建的,以及如何运行的。

那首先呢,我们来看看线程的创建,实际这一页PPT啊,我们在之前已经向你做过介绍了,那我们都知道,对于不同的平台,在创建线程时使用的API都是不一样的,对吧?对于linux以及mac来说呢,它使用的是p three create来创建线程,而对于WINDOWS来说呢?它使用的是create three的来创建线程zur rtc,由于它是支持多平台的。

所以,对于不同平台的版本,它的代码也是不一样的。那这些呢,我们都需要了解。

今天呢,我们就来详细看一下p three的create以及create three的这两个API。

它的一些具体参数首先我们来看一下WINDOWS下的create three它都有哪些参数。那我们从这里可以看到啊,对于create three的API来说,它包含的参数还是蛮多的,一共有六项,

那第一项呢,是线程的属性。那一般情况下,这个参数呢?我们都设成了NULL,也就是说采用默认的线程属性,

第二个参数呢?是线程创建好之后使用的堆栈大小,如果我们设置成零,它也是使用默认的堆栈大小。那如果我们对这个堆栈啊,有特殊的要求,你可以在这里进行设置

第三个参数就比较重要了。那它表示的是我们线程启动之后要执行哪个函数,所以这里呢是一个函数地址。

那第四个呢?是参数什么参数呢?就是我们前面设置的这个函数的输入参数。对于这个函数来说呢,它只能有一个输入参数好,

再接下来create flags表示的是创建时设置的一些flag。那通常情况下,我们也设置成零零表示,当线程创建好之后,就立刻参与线程的调度,所以一般情况下呢,我们都将它设成零。

好,最后一个参数,如果我们在创建线程的时候设置了这个参数,那当线程创建好之后就会给我们返回一个线程ID。它呢,其实是一个输出参数,对吧?那下面呢?

我们来看一下web rtc是如何使用create three的这个API的。我们这个例子中所展示这样,那这个呢?就是我从外边tc中拷贝的一段代码,

那它第一个参数呢?就成了。采用默认的线程属性。第二个参数呢,是零使用默认的堆栈大小。好,第三个参数是pre run。代表要执行哪个函数?第四个呢?是这个函数的输入参数web rtc呢?是将这次传给preturn,第五个参数设成零,就是创建好之后呢,立即参与调度。最后一个参数呢,就是获取我们创建好的线程ID。

这就是create three的API,那接下来呢,我们再看看linux下的p three的create这个API。对于这个API来说呢,它的参数相对少一些,一共有四项对吧?

那第一项呢是p three的杠t类型的。它表示,当我们创建好线程之后呢,在这个变量中存放一些关于这个线程的基本信息。这是第一个参数,

第二个参数呢?也是线程属性,一般情况下呢,我们只要使用p three的相关的API。给它做一下初始化就OK了,也就是说采用默认的属性。

第三个参数与create three的是类似的,也是一个执行函数。那这个执行函数呢?

也有一个输入参数,也就是第四个参数arg,那这个呢?就是p three的create这个API。

那我们接着来看一个例子。那对于这个API来说呢,

它的第一个参数是我们创建的p three的杠t类型的一个变量地址,对吧?

那第二个参数呢,是我们创建的的杠attr杠t类型的一个变量地址。

第三个参数是一个执行函数,

最后一个参数呢?一般是我们当前的这个对象,

这就是p three的create API。好,那了解了这两个API之后呢?下面我们再来看看,当我们程序运行起来之后,它的一个基本逻辑是什么?

其实这一块儿啊,我在前面儿介绍常见的线程模型的时候,我已经向你做过基本的介绍了,其实在执行函数的内部。就是一个外循环,而且是一个死循环,那么在这个循环中,它做什么事儿呢?就做两件事儿,第一件事儿呢,就是从堆列中。取出message消息。当拿到这个消息之后呢,调用dispatch来处理这个消息。(执行函数,拿到消息,让谁处理)

我们再来看看dispatch,在dispatch中它会调用这个消息的handler对象的on message进行逻辑处理。同时,把正在处理的这个消息呢,当做参数。传给内部呢,它就会对这个消息做相应的逻辑处理了,那具体是怎么做的?

每一个消息有不同的逻辑,这就是把控制权交给了发送线程。发送线程在执行逻辑的时候,他是知道他要做什么事的。但是这个事儿呢,他不想自己做,他想交给其他的线程做,

让自己做更重要的事情,对吧?但遇到的一个困难是,执行线程不知道发送线程,它要执行怎样的逻辑?那最好的方法就是让发送线程,把要执行的逻辑写好之后呢,把这个任务交给执行线程,那执行线程 只要按照发送线程的要求。去执行任务就OK了。

这就像我们日常中的管理一样,那对于一个项目管理者来说,你想让你底下的开发人员去帮你完成一个大的项目。由于你对这个项目特别熟悉,所以呢,

你应该把这个项目怎么做?做哪些功能告诉开发人员,这样开发人员按照你的要求把这个代码编写出来就了,对吧?那这也是我们工作中经常采用的一种工作方式,

那实际上在我们的开发项目中啊,你会发现很多的系统的管理都是我们日常生活中的工作方式。只不过说你把日常中的工作方式转换成了代码,让计算机去做这一系列的事,也就是说计算机模拟了我们日常的工作。

那了解了这些内容之后呢?咱们现在切换到Windows系统下来看一下外拔tc的代码是如何实现的?我们把程序运行起来,依然是以peer connection clan的。这个项目为起点,

这样呢,一步一步进入到y八七c的底层。来找到我们创建线程执行线程的地方。好,我们先连接新的服务器,选择我们要连接的对象。这时候呢,首先会创建peer connection factory,对吧?这个在我们之前都介绍过。之后呢,在peer connection factory内部跳到三大线程的创建。网络线程工作线程限定线程对吧?那在这里呢,我们不需要看过多的内容,

owned_network_thread = rtc::Thread::CreateWitthSocketServer只是创建了一个对象,具体创建什么线程以及线程与对象的绑定都是在start的时候做的

我们只要关注。代码的91行的这个函数,看它内部是如何实现的就了好,我们单步执行。现在呢,跳入到这个函数中OK,这样呢,我们就进入到了的这个函数中,我们来详细看一下,在这个函数中具体做了哪些事情?125行呢,它首先判断现在这个线程是否已经运行了,对吧?如果运行了,我们现在这个逻辑呢,

就没必要再做了。直接退出就OK了,那显然现在是没有运行的。好,那紧接着呢,它调用了一个restart,这个restart的含义呢,就是将线程的状态机重新复位。那感兴趣的同学呢?可以再跳到这个函数中去看一下具体的事件,这里呢?我们就不详细跟踪了,好,我们继续往下执行。再接下来,

它是创建了一个three的manager实例,对吧?那在这里之所以要执行这条语句呢,是为了确保。street manager已经创建成功了,那在这里的注释呢?也说的非常明显。确保我们在创建一个新线程之前呢?在主线程已经创建了thread manager这个对象。OK,那我们继续走啊。到737行,那这个时候呢,我们就看到了一个非常熟悉的函数,就是create three的。

那通过这个函数呢,我们就将一个线程创建出来了,线程创建成功之后呢,它就会执行这个方法。那下面呢,我们就跳到pre ran,然后来看看在pre ran中做了哪些事情,我们在这里打个断点啊。那当断点来到824行的时候。实际我们的新线程就已经创建成功了,对吧?因为它已经执行到pre ran这个函数中了OK,那下面呢?我们来单步执行一下。在这个函数中呢,

首先它将传入的参数恢复成。three的对象。之后呢,调用thread manager中的set current three的方法将这个线程呢设置到thread m anager中。那这里啊,我们大家要注意,就是我们创建好的three的对象,什么时候与我们的线程进行绑定的呢?一定是在这个线程已经启动之后才进行绑定的,对吧?这里呢,就是这种情况,就是当我们的线程运行起来,执行了pre ran之后。它才能将当前线程与我们之前创建好的的对象进行绑定,

对吧?其中825行就是做这件事儿了,绝对不可能是我们线程没有创建的。就进行线程的绑定,这是不可能的,这就成了无米之炊了,对吧?OK,那我们再继续往下走,再下来呢,是给这个线程设置一个名字,我们就不看了,之后呢,将three的对象赋值给当前的任务队列。因为本身three的就是一个队列。

所以就是让我们当前的这个任务队列呢,指向three的也可以这么理解,那执行到831行之后呢,我们就看到。当前线程呢,又拐了一个弯到three的对象的run方法中去执行,我们再跳到这个run方法中。在run方法中呢,它只调用了一个函数,就是process message对吧?我们继续跳进去。那在process message中做了什么事情呢?这块的逻辑啊,就非常简单了,在993行,

我们就看到了这个while循环。那在这个死循环中,他做了什么事呢?非常简单,首先通过get从消息队列中获取一个消息。之后要用dispatch来处理这个消息,在dispatch中呢,它写了一堆逻辑对吧?但核心的逻辑。就是664行。

也就是调用p message中的handler对象的on message方法。来对这个消息呢进行处理,具体之中的逻辑只有发送线程才知道,如果我们想知道之中具体做了哪件事儿?(on->message是发送线程的函数,具体要看发送线程怎么搞)

我们就要找发送线程,在构造这个消息的时候。它里边儿的on message具体是如何实验的,我们才能知道里边儿的逻辑是什么,对吧?通过这样一个分析啊,我们可以知道,对于y八七c来说。它各县城的职责其实是非常明确的,每个县城有每个县城要做的事情,每个县城有每个县城的责任。

工作线程我只负责做事儿,对吧?

那信令线程呢?我要与应用层进行交互。

网络线程呢?我负责网络包的收发,这样就使得各线程的工作效率非常的高效。

但同时,它也带来一个问题,就是我们在阅读它代码的时候。就非常的困难,因为当我们进入到工作线程去看它的逻辑的时候,你会发现里边没有什么具体的业务逻辑。

真正的逻辑是在哪儿呢?是在发送线程,有可能是限令线程,有可能是网络线程,他们才知道具体要干什么活儿。

而对于工作线程来说呢,他只是埋头干活儿的人,并不知道具体逻辑是什么,别人让我怎么做,我就怎么做。那这就打破了我们人类思维,从上到下的一个惯性,它实际要跳着来。也就是说,如果我们想知道具体要做哪些事情的时候,你要找到它的根源才能清楚这件事儿到底在做什么,为什么要这样做?

而对于干活的县城来说呢,它是一问三不知。所以这也是我们为什么学习web rtc有这么多困难的一个非常关键的原因,(最主要的一点其实是讲了webrtc的工作线程只是调用了发送线程的接口而已)

好,那通过这节课呢,我们就知道了web rtc的线程是如何创建出来的,以及在线程内部,它是如何运行的。

同时,我们也知道了,我们要想弄清楚各线程之间的逻辑,必须从发送线程着手,而不能从工作线程着手。这样才不至于我们在庞大的代码里迷失方向,

那以上呢,就是我们这节课所介绍内容有任何的问题呢,你可以到讨论区或者是群里去给我留言。我在那里呢,给你做相应解答好,谢谢。

相关推荐
egekm_sefg1 天前
webrtc学习----前端推流拉流,局域网socket版,一对多
前端·学习·webrtc
红米饭配南瓜汤1 天前
WebRTC服务质量(11)- Pacer机制(03) IntervalBudget
网络·网络协议·音视频·webrtc·媒体
布兰妮甜1 天前
使用 WebRTC 进行实时通信
javascript·webrtc·实时通信
星就前端叭2 天前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
web150854159352 天前
vue 集成 webrtc-streamer 播放视频流 - 解决阿里云内外网访问视频流问题
vue.js·阿里云·webrtc
m0_748232392 天前
WebRTC学习二:WebRTC音视频数据采集
学习·音视频·webrtc
m0_748237152 天前
WebRTC 环境搭建
webrtc
重生之豪哥2 天前
WebRtc webrtc-streamer部署
webrtc
Amarantine、沐风倩✨3 天前
设计一个监控摄像头物联网IOT(webRTC、音视频、文件存储)
java·物联网·音视频·webrtc·html5·视频编解码·七牛云存储
mo47763 天前
Webrtc音频模块(四) 音频采集
音视频·webrtc