《C++多线程编程》学习笔记
Posted 看,未来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《C++多线程编程》学习笔记相关的知识,希望对你有一定的参考价值。
文章目录
以什么形式来总结这篇呢,哎,这种学习笔记类型的不是第一次写,但是没有一篇逃过被“拆散”的命运。
不管了,先罗列着,说不准哪天这篇就没掉了,那就是我完全吸收了。
出自《Linux多线程服务端编程 – 使用muduo网络库》
线程安全的对象生命期管理
当析构函数遇上多线程
对象的创建
对象池
线程同步精要
按重要性排列:
1、尽量最低限度的共享对象,减少需要同步的场合。
2、使用高级的并发构件,如 TaskQueue、Producer-Customer、Queue、countDownLatch 等。
3、只使用非递归的互斥器和条件变量,慎用读写锁,不要用信号量。
4、除了使用atomic整数之外,不要自己编写 lock-free 代码,也不要用 “内核级” 同步原语,不凭空猜测 “哪种做法性能会更好”。
对于第一点,一时间想不到哪些地方需要共享对象,全局表、单例、还有哪些?对象传参吗?
对于第二点,emmm,没有试过,都是自己写的。
对于第三点,特地查了一下,可重入锁我还没学过呢。信号量,不喜欢。读写锁,emmm,一言难尽,不知道是好是坏,不做评判。
对于第四点,哈哈笑死我了,有那么一些人,他们做事情就一半靠现实,一半靠猜的哈哈。江山易改本性难移。反正我是不喜欢这种靠自己猜的,在我妹面前我也是想方设法让她知道一定要求证之后才能说,不然就说不知道呗,不知道又没啥丢人的,现在不知道,不代表以后不知道,知道自己不知道,才能知道。当然她有没有听进去我就不知道了。
互斥器(mutex)
用RAII手法封装mutex的创建、销毁、上锁、解锁。
性能注脚:Linux 的 pthread mutex 采用 futex 实现,不用每次加锁、解锁都陷入系统调用,效率不错。
条件变量(condition variable)
慎用读写锁
书里面说不要用,我也不知道该怎么说,反正先听着,具体咋回事咱也不知道。不能因为人家有名气就说啥都是对的呀,mysql数据库里边不还使用了读写锁嘛。
为什么要慎用读写锁,这点作者讲的很好,我赞同(我也不喜欢用读写锁哈哈)
1、正确性。我个小白我怎么知道什么时候会不会犯迷糊拿着读锁改数据啊。那除非我在读锁临界区内调用的函数、变量全都是immutable的,不然我真不敢打包票。
2、性能。
谁说 mutex 性能就不好了?谁说的?mutex对资源的消耗更多的是在锁争用,Linux 的 pthread mutex 采用 futex 实现,不用每次加锁、解锁都陷入系统调用,效率不错。
读写锁呢?准备写的时候,需要等待所有读锁被释放,这个问题不大,反正读的很快,不要跟我说拿着读锁去读文件去了。准备读的时候呢?需要从什么时候开始排队?从写的时候开始排队吗?不,从申请写锁的时候,后面的读请求都得排队,直到释放写锁。
上面MySQL里面使用读写锁的理由如果不能服人的话,那看一下这个理由:
书里给了一个用mutex替换读写锁的例子,为了保持线程的并发与安全,采用了将需要写的数据拷贝一份副本,对副本进行修改之后进行替换。
我咋觉得这里有点像脏读啊。
这个副本操作在muduo的源码EventLoop里面倒是有看到,用的很漂亮,但是不代表啥场景都好用,还是要分场景吧。
如果是数据库场景:某一个元数据,存在1000个读访问,这时候来了一个写访问,难道能让这些读操作和写操作公平竞争?这不该先写吗?
我就这句话:看场景吧。
多线程服务器的适合模型与常用编程模型
one loop per thread + thread pool
IO事件用EventLoop,线程的数目基本固定,可以在程序启动的时候设置,不必频繁的创建和销毁。
对于没有IO任务而光有计算任务的线程,使用blocking queue实现的任务队列,放线程池里面运行。
我现在也很喜欢这种模式了。
进程间通信只用TCP
这个,这个,这个我是很赞同啦,但是这种话我还是不敢说的。
再看吧,反正那几种通信方式我都会。我也觉得,这个分布式的浪潮打过来,TCP通信成为进程间通信的主流也是早晚的事情吧,除非出现了一种比网络通信更好的跨主机通信方式(插根网线吗哈哈哈(好像也不是不行哦))。
C++多线程系统编程精要
来看这么一段代码:
if(node && node->next){
node = node->next;
}
有问题吗?没有问题。放在单线程下是没有问题的。
但是现在在讲多线程,我们的思维就需要拔高一下,从二维世界提升到三维世界。
如果我现在运行到了 if(node ,线程被切出去了,在另一条线程里面把这个 node 给挪空了,或者干脆析构了,然后我切回来的时候,接着运行,node->next,好了,段错误。
既然踏上这条路,我们就要做好线程可能被随时切出去的觉悟,CPU乱序执行。
所以单例模式才会有双重锁定的这一设定。还真以为是同一时间两个线程在抢锁啊,哪儿那么刚好。
如果就一重锁定,刚突围就被叉出去了,回来之后早都物是人非事事休,欲语泪先流了。
Linux上的线程标识
pthread_self() 函数返回了当前线程的标识符,类型为 pthread_t,然而我是体验过这个类型的无语之处的,它,不知道是什么类型哦。
这就是我前面说的,它应该是这个类型,或者是那个类型,又可能是另一个类型,反正就是不给你确定,气死你。
那就:
1、无法打印输出
2、无法写入日志
3、无法做哈希表键
4、无法判断当前线程是否拥有某一把锁
采用 gettid() 函数吧,好处:
1、返回值类型为pid_t,通常是一个小整数
2、方便定位
3、全局唯一
不要乱开线程
不要为一些不重要的边缘操作开多线程操作,比如写日志。
试想一下,一个八核CPU,开一条线程写日志,卡死了,死了一核,还有七核。开了三条线程,死了,死了三核。
不要为了一些不重要的边缘操作专门开线程操作,完全可以几个不重要的操作挤一挤共用一个线程嘛。
核心操作不要吝啬资源。
以上是关于《C++多线程编程》学习笔记的主要内容,如果未能解决你的问题,请参考以下文章