《Linux多线程服务端编程》笔记——C++多线程系统编程精要

今天顺着读了第四章,同样跟着老师的角度梳理多线程的问题。
前面老师分析了一些编程的细节问题,比如线程安全和创建销毁等问题,不详述。

多线程与IO

这个是之前博文聊过的东西,但没有深入。现在重新拿出来说一下。多线程网络编程的IO处理是个值得讨论的事情:如何IO,能否多个线程同时操作同一个文件描述符。答案自然是否定的,由于多线程的操作进度和效率未知,所以我们无法知道其先后顺序。所以会出现一些难以解决的问题。
在这里插入图片描述
作者描述的这些情况都是多线程会出现的问题,还有:
在这里插入图片描述
其实这个问题我之前在看华为软挑的一位大神(忘了是谁不好意思)分析过:用多线程去读数据不一定能加快读取速度,所以,多线程并不值得用在提升读取速度这里,读取速度很多时候并不是效率瓶颈,所以不值得花费时间在这个上面
接着老师的分析,每个磁盘是有一个操作队列的,在内核里多线程读写时排序执行的,而内核缓存的热数据,才是多线程可以真正提高效率的地方。而且不容易出错,所以一个文件只由一个进程的一个线程来做是没有问题的。
进入服务器编程中:每个文件描述符只由一个线程操作,便可以解决上面提到的问题。一个线程可以操作多个文件描述符,但是不能操作别的线程拥有的文件描述符。其实也就是单线程逻辑多线程使用,单线程的顺序问题就无需我们考虑,所以我们把单个的放在单线程逻辑下,而后增加线程来处理更多的文件描述符,这是多线程提升效率的稳健方法。

用RAII包装文件描述符

Linux的文件描述符是小整数,0是stdin,1是stdout,2是stderr。所以我们打开的新文件是从3开始的。POSIX要求每次都取最小可用的文件描述符。
所以这时就会出现上述问题,当我fd8收到了耗时的请求,然后开始处理,他记住了处理完要返回给fd8,但是处理的过程中,原fd8由于某些原因关闭了。而一个新的文件被打开,占用了此8号位,处理完成后,发现fd8还在,于是返回给fd8,但这时fd8已经不是原先的了。(有点像发送验证码填错了手机号,收到验证码的人会一脸懵逼,这是个啥?)
所以C++里解决的办法就是RAII,其实说到底,RAII是个啥。在我看来,其实就是一个单线程逻辑的东西,我把顺序需要统一,互相依赖或者易出错的逻辑封装为一个小的单线程逻辑,这样就不会出错,而且逻辑的粒度较小,在整个多线程逻辑中不会出现很大的延迟。作者的意思是:socket对象封装,所有此文件描述符的操作都在此对象中完成,对象析构的时候释放此文件描述符。也就是说,一个文件描述符的操作在一个对象周期,也就是单线程逻辑中完成。
非阻塞经常会遇到的就是这个问题,由于非阻塞为其他请求腾出地方,难免会出现错误导致自己关闭,所以在开始就要将所有的操作绑定起来,以免出现错误。
在这里插入图片描述
这时作者举得例子。其实将TCPconnection随着TCP连接关闭之后,原处理就已经失去了方向。所以这就是之前提到的shared_ptr的作用了。我用shared_ptr管理对象就能轻易的获取对象是否存活。

总结

后面还提到一些关于fork和signal的分析,不详述,可自行观看原文。
其实多线程编程中需要重视的就是线程:

  1. 线程是宝贵的,每一条线程创建出来就会有自己的开销,所以无限制的云运行太多线程是不可取的,反而增大了CPU的负担。
  2. 创建和销毁都是由代价的,之前我们已经提到过,什么时候创建和销毁不会影响整体性能时,就认为是可以开一个线程的。
  3. 每个线程有自己的职责,而且只负责自己的任务。否则很容易发生就像我们之前说的任务错乱导致结果不对。
  4. 线程间的交互尽量简单,越复杂越容易发生错误。比如锁的区域和个数尽量少。

多线程编程中最容易出问题的就是各个线程之间的竞争,由于多线程我们无法知晓每个线程的进度,所以我们只能用程序逻辑宏观上给限制,然后用各种条件或者信号来完成线程间的协作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值