热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

WebRTC源代码探索之旅——多线程篇1

转自:http:blog.csdn.netkenny_zharticledetails38580919随着CPU频率接近物理极限,多芯片、多核几乎成为了

转自:http://blog.csdn.net/kenny_zh/article/details/38580919


随着CPU频率接近物理极限,多芯片、多核几乎成为了加速软件运行速度的唯一选择。与之相应地,多线程、异步编程以及并发编程成为了软件开发人员的必修课。因此,各种各样的开发框架不断涌现。在C++领域,boost的thread库等优秀的多线程框架也是其中的代表。特别是针对socket网络编程的boost.asio库可以轻松帮助开发人员实现多线程大量并发的网络服软件。

 

与这些框架相比,WebRTC的多线程模型还是比较特别的。它的核心是类似于WSAWaitForMultipleEvents的多路信号分离器。这是我最喜欢的多线程模型。它使每条线程既可以处理消息(post、send),同时也可以处理多个IO。并且在必要的情况下让一条线程独占(管理)一些资源,避免过多的使用锁造成线程死锁。个人认为这种模型可以帮助开发人员更容易地实现快速响应的界面程序。

 

WebRTC的线程模块放在/trunk/talk/base目录下,namespace使用talk_base::。主要涉及criticalsection、event、messagequeue、thread、messagehandler、physicalsocketserver等文件。顺便提一下,现在网上仅有的几篇关于WebRTC或是libjingle的文章多数从Sigslot开始讲解,但是我不打算现在对Sigslot进行展开,因为它还是很好理解的。不明白的朋友可以上网查询一下,或者姑且就把它当做函数回调(更精确地说它实现了C#的delegate)就可以了。在talk_base的多路信号分离器中只有少数几个地方使用了Sigslot,即使忽略它也不影响对多路信号分离器的理解。

 

1 event

 

event.h/event.cc文件中只有talk_base::Event类。

 

1.1 talk_base::Event

 

该类主要实现了跨平台的Win32 Event功能(正如前言中所说,本文假定读者已经很熟悉Win32平台的各种组件,如有不明白的地方可以参考MSDN)。talk_base::Event类的各个成员函数与Win32 Event所提供的API几乎一致,所以不再多做解释。

 

在Linux系统中,WebRTC使用了mutex和条件变量来实现Event的功能。首先,我将对Win 32 API和pthread API做一下类比:

 

CreateEvent

pthread_mutex_init、pthread_cond_init这两个函数用来创建pthread的mute和条件变量。

 

CloseHandle

pthread_mutex_destroy、pthread_cond_destroy这两个函数用来销毁pthread的mute和条件变量。

 

SetEvent

pthread_mutex_lock、pthread_mutex_unlock这两个函数加锁和解锁mutex,pthread_cond_broadcast函数用来解除所有等待在该条件变量上的线程的阻塞状态。

 

ResetEvent

pthrea_mutex_lock、pthread_mutex_unlock(已解释)

 

WaitForSingleObject

pthrea_mutex_lock、pthread_mutex_unlock(已解释)。pthrea_cond_wait函数用来使线程阻塞在条件变量上。

 

下面我将大致解释一下talk_base::Event类的实现原理:

 

talk_base::Event的主要功能由条件变量实现,mutex只是辅助条件变量起到锁的作用。条件变量的pthread_cond_wait和pthread_cond_broadcast函数与Win32 Event的WaitForSingleObject和SetEvent基本类似。talk_base::Event是否为signal状态由布尔类型的成员变量event_status_控制。是否为manual reset的Event由布尔类型的成员变量is_manual_reset_控制。与Win32 Event不同的状况主要体现在Event的manual reset控制上。

 

Linux系统下所有调用talk_base::Event::Wait函数的线程会阻塞在pthread_cond_wait函数上。当talk_base::Event::Set函数被调用时,pthread_cond_broadcast函数会解除所有等待在pthread_cond_wait函数上的线程的阻塞状态。这对于manual reset的Win32 Event来说没什么问题,问题出在auto reset的Win32 Event上。Auto reset的Win32 Event每次只能解除一条等待在Event上的线程的阻塞状态,其他线程依然为阻塞状态。这就需要mutex来配合实现了。

 

在这里要重点解释一下pthread_cond_wait函数的第二个参数pthread_mutex_t *mutex。当线程进入pthread_cond_wait函数时会解锁mutex,而在离开pthread_cond_wait时会重新加锁mutex。可以理解为:

 

[cpp] view plaincopy
  1. int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex)  
  2. {  
  3.   pthread_mutex_unlock(mutex);  
  4.   …  
  5.   …  
  6.   …  
  7.   pthread_mutex_lock(mutex);  
  8.   return 0;  
  9. }  


这是Win32 没有的行为,需要特别注意。

 

有了以上的机制后,模拟auto rest的Win32 Event就没问题了。当第一条线程获得mutex锁并离开pthread_cond_wait函数时,其他线程会依然被阻塞在pthread_mutex_lock(mutex)函数上,无法离开pthread_cond_wait函数。那条成功离开线程会马上检测当前的talk_base::Event是否为manual reset的,如果不是就马上将event_status_成员变量设置为false,并解锁mutex。这时其他线程才能有机会离开pthread_cond_wait函数。不过当他们离开pthread_cond_wait后立即检测event_status_成员变量,如果为false就重新调用pthread_cond_wait函数。这就完美实现了Win32 Event的auto reset的语义。

 

条件变量和mutex的配合是talk_base::Event类的难点。如果读者还是不能完全理解,请仔细阅读以上3段的内容(也可以上网查找pthread_cond_wait函数),并结合event.cc的源代码反复揣摩,应该可以很快理解的(毕竟代码不多,而且也不是WebRTC中真正困难的部分),我就不再多做解释了(在后面的部分我会提供独立编译\trunk\talk\base\目录下的源代码的makefile脚本,读者可以编译后添加调试代码分析talk_base::Event的原理)。

 

在分析了talk_base::Event的源代码之后有个疑问,那就是为什么不用pthread_cond_signal函数实现Win32 Event的auto reset语义?由于时间的原因暂且不做深入地研究。



推荐阅读
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了软件测试知识点之数据库压力测试方法小结相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Centos下安装memcached+memcached教程
    本文介绍了在Centos下安装memcached和使用memcached的教程,详细解释了memcached的工作原理,包括缓存数据和对象、减少数据库读取次数、提高网站速度等。同时,还对memcached的快速和高效率进行了解释,与传统的文件型数据库相比,memcached作为一个内存型数据库,具有更高的读取速度。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
author-avatar
手机用户2502885647_951
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有