《C++多线程编程》学习笔记

Posted 看,未来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《C++多线程编程》学习笔记相关的知识,希望对你有一定的参考价值。

以什么形式来总结这篇呢,哎,这种学习笔记类型的不是第一次写,但是没有一篇逃过被“拆散”的命运。
不管了,先罗列着,说不准哪天这篇就没掉了,那就是我完全吸收了。

出自《Linux多线程服务端编程 – 使用muduo网络库》


线程安全的对象生命期管理

当析构函数遇上多线程

C++编程经验(13):当析构函数遇上多线程

对象的创建

求解惑:关于多线程并发环境下构造函数的线程安全

对象池

关于单例模式与对象池的思考


线程同步精要

按重要性排列:

1、尽量最低限度的共享对象,减少需要同步的场合。
2、使用高级的并发构件,如 TaskQueue、Producer-Customer、Queue、countDownLatch 等。
3、只使用非递归的互斥器和条件变量,慎用读写锁,不要用信号量。
4、除了使用atomic整数之外,不要自己编写 lock-free 代码,也不要用 “内核级” 同步原语,不凭空猜测 “哪种做法性能会更好”。

对于第一点,一时间想不到哪些地方需要共享对象,全局表、单例、还有哪些?对象传参吗?
对于第二点,emmm,没有试过,都是自己写的。
对于第三点,特地查了一下,可重入锁我还没学过呢。信号量,不喜欢。读写锁,emmm,一言难尽,不知道是好是坏,不做评判。
对于第四点,哈哈笑死我了,有那么一些人,他们做事情就一半靠现实,一半靠猜的哈哈。江山易改本性难移。反正我是不喜欢这种靠自己猜的,在我妹面前我也是想方设法让她知道一定要求证之后才能说,不然就说不知道呗,不知道又没啥丢人的,现在不知道,不代表以后不知道,知道自己不知道,才能知道。当然她有没有听进去我就不知道了。


互斥器(mutex)

用RAII手法封装mutex的创建、销毁、上锁、解锁。

性能注脚:Linux 的 pthread mutex 采用 futex 实现,不用每次加锁、解锁都陷入系统调用,效率不错。


条件变量(condition variable)

while 如何解决虚假唤醒 及 if 为什么就不行?


慎用读写锁

书里面说不要用,我也不知道该怎么说,反正先听着,具体咋回事咱也不知道。不能因为人家有名气就说啥都是对的呀,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++多线程编程》学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

C++ 多线程学习笔记:读者-写者问题模拟

多线程编程学习笔记——线程同步

多线程编程学习笔记-基础

多线程编程学习笔记——线程同步

多线程编程学习笔记——线程池

多线程编程学习笔记——使用并发集合