并发问题产生在哪些地方?
在探讨并发原理之前,我们要先明白并发问题的产生原因和产生的地方。如果这些我们不清楚的话,怎么能解决好并发问题呢。首先,我们先观察下面两张图:
问题1: 上图中 图1和图2中的代码执行逻辑,哪个坑会有问题?
非常明显,图2中的多个CPU同时执行一段代码的时候,是非常容易有问题。当两个CPU交叉去执行同一段代码的时候,非常容易出现问题,
通过上图,我们可以总结出来,并发问题产生的根本原因,就是 CPU穿插执行程序
。
也就是说:并发问题就是 多个CPU同时操作同一个可读可写的数据
。
如何解决并发问题?
从上面我们得出的结论,就是多个CPU同时操作同一个可读可写的数据。我们想想,如果一个CPU读写完成,另外一个CPU再进行读写操作。这些并发问题就能完美解决。那么我们思考一下,CPU读写完成,是不是就是一个原子操作。一段操作,要么全部执行,要么全部不执行。
我们看下上图,CPU要从内存中获取数据,必须通过控制总线,获取数据在内存中的地址。如果,我们给控制总线进行加锁,只能同一时间只有一个CPU占用控制总线,这样就能保证CPU的原子操作了。
上面对控制总线加锁的方案,确实能够解决并发问题,但是又引入另外一个问题,性能问题: 控制总线是整个系统的资源,同一时间只能有一个CPU使用该资源,多核CPU就变得无意义了。
控制总线加锁,导致系统性能严重下降的根本原因: 对控制资源加锁,锁太重了,导致系统资源严重下降。
知道原因之后,那么可以进行降低锁粒度,在CPU内部,加上一层缓存,对缓存行中的数据进行加锁,不影响控制总线的使用。如下图:
并发产生的地方
上图是从语言层到CPU层,核心流程图。我们从上图来思考一下,产生并发问题的地方都在哪些地方?
第1处: CPU层次
CPU执行指令片段,如果不进行指令片段的原子操作,那么CPU执行的结果是无法保证的。因此CPU执行代码指令会产生并发问题的。
第2处: OS层次
当不同进行进程进行系统调用的时候,OS需要调度各种资源,系统调用肯定会存在对共同资源的竞争。OS系统调用过程中,也会产生并发问题。
第3处: 进程层次:
在linux中,进程的资源是相互独立,互补影响的。不会存在竞争问题。
第4处: 线程层次
线程是轻量级的进程,线程在进程中资源共享。如果有公共资源,线程之间进行竞争,会发生并发问题的。
综上所述: 并发问题只会存在 CPU层次,OS层次,和我们应用的线程层次。现在CPU和OS已经解决了自己并发的问题: CPU提供了原子指令,OS 中有pthread等函数。而我们开发者只需要处理好应用层次的线程间的并发问题即可。
线程并发问题处理方案
核心原理
在代码段中设置一个标志位,线程竞争标志位,当获取成功之后,去执行相应的代码段。当获取不成功之后,需要进入等待队列。这个时候,进入等待队列,也需要进行竞争(多个线程没有获取成功),所有,需要对队列的tail指针进行加锁,加锁成功,进入阻塞队列,加锁失败,进行自旋,一直能够加锁成功,进入队列。之前加锁成功,执行完代码之后,会唤醒阻塞队列中的线程,让阻塞队列中的线程重新进行标志位加锁,循环往复,直到执行完代码,线程销毁。
以上就是本次分享的所有内容,如有不足,请多多指正。