C++多线程系统编程精要(二)

1. 多线程与IO

  • 多个线程分别读或者写用一个磁盘的多个文件并不见得会提速,因为每块磁盘都有一个操作队列,多线程的 读写请求到了内核是排队执行的,只有在内核缓存了大量数据的情况下,多线程读这些热数据才可能比单线程快。
  • 多线程磁盘IO 的一个思路将每个磁盘配备一个线程,把所有的针对此磁盘的IO操作挪到一个线程,减少内核中的锁争用。
    一个文件只由一个进程中的一个线程来读写.

多线程应该遵循的原则是: 每个文件描述符只由一个线程操作,从而轻松解决消息收发的顺序问题,避免了关闭文件描述符的各种race condition。一个线程可以操作多个文件,但是不能操作其他线程拥有的文件描述符。

特列: 对于UDP,协议本身保证消息的原子性,在适当的条件下可以多个线程同时读写同一个UDP文件描述符。

2. 用RAII包装文件

在系统刚刚启动时,文件描述符0表示标准输入,1表示标准输出,2表示标准错误。
RAII (Resource acquisition is initialization)资源获取即初始化。用Socket对象包装文件描述符,只要Socket对象还活着,就不会有其他Socket对象跟其拥有一样的文件描述符,不可能串话。

利用c++构造的对象是最终被销毁的这一原则,RAII的做法是使用一个对象,在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,最后在对象析构的时候,释放构造时获取的资源。

3. RAII与fork()

fork()之后,子进程会继承父进程的地址空间和文件描述符。子进程不会继承

  • 父进程的内存锁
  • 父进程的文件锁
  • 父进程的某些定时器

3. 多线程与fork()

多线程与fork()的协作性很差,fork()不能在多线程程序中调用,因为Linux的fork()只克隆当前线程的thread of control,不克隆其他的线程。 fork()之后,除了当前线程,其他的线程都消失了。不能fork()出和父进程一样的多线程子进程

fork() 之后,子进程就相当于处于signal handler(信号处理函数)之中,不能调用线程安全的函数(除非其是可重入的),而只能调用异步信号安全的函数。fork() 之后,子进程不可调用:

  • malloc,因为malloc 在访问全局状态是几乎肯定会加锁。
  • 任何可能分配或释放内存的函数,new,、、、
  • 任何pthread 函数,不能用pthread_cond_signal 通知父进程,只能通过读写pipe来同步
  • printf系列函数,因为其他线程可能恰好持有stdout/stderr的锁

因此安全的做法是在fork() 之后立即调用exec()执行另一个程序。彻底隔断子进程与父进程之间的联系。

3. 多线程与signal

在signal handler(信号处理函数)之中只能调用可重入函数。具体内容见连接https://blog.csdn.net/YoungSusie/article/details/92842941

总结

编写多线程c++程序的原则如下:

  • 线程是宝贵的,一个程序可以使用几个或者十几个线程。
  • 线程的创建和销毁是有代价的,一个程序最好在一开始创建所需的线程,并一直反复使用。
  • 每个线程应该有明确的职责
  • 线程之间的交互应该尽量简单,理想情况下,线程间只用消息传递(Blockingqueue)方式交互。
  • 要考虑清楚一个mutable shared (可变共享)对象将会暴露给哪些线程。

分类 网络编程 学习笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值