高并发编程之基础概念

  在向高阶程序员进阶时,多线程开发是比不可少的过程,而多线程开发也是程序开发中的比较复杂的一块,下面就简单介绍一些多线程开发的概念。

为什么需要并行

  • 业务要求
    在一些开发中我没也许需要去执行两个模块,但是两个模块之间有互相的调度,那么这时候如果使用同步线程去写的话就会变的异常的复制,程序员不仅仅需要去写业务方面的逻辑,而且还需要注意模块与模块之间的调度问题,所以此时如果我们选择使用异步线程去处理的话,模块之间的调度问题可以直接交给操作系统去处理,而程序员只需要去关注业务方面的东西。
  • 性能
    多线程程序在多核cup上面性能一般是比单线程程序性能要好。尤其是在计算密集型程序中是得到广泛的应用。比如图形处理,数据挖掘等等。
  • 摩尔定律失效
    摩尔定律:预计每18个月,单核cpu性能将会提高一倍。
    2004年,Intel宣布取消4GHz计划。
    虽然现在已经出现了主频为4GHz的cpu,但是在更新换代方面已经 远远的落后于摩尔定律。单核cpu的发展已经出现了瓶颈。此时多核cpu就诞生了,由于多核cpu的出现,并发也应运而生。顶级计算机科学家唐纳德·尔文·克努斯也说过“在我看来,并发这种现象或多或少是由于硬件设计者已经无计可施,他们将摩尔定律失效的责任推脱给了软件开发者。”

同步(synchronous)和异步(asynchronous)

同步异步一般是说方法调用,同步执行方法,只有当方法执行 结束之后才进行返回,而异步执行方法,一般是瞬间返回,但是在后台另外启一条线程去执行这个异步方法。
下图中,横向箭头即表示单线程,也表示一个时间轴,同步方法执行必须要等到方法执行结束,而异步方法则瞬间返回。

并发(concurrency)和并行(parallelism)

  许多人认为并发和并行是一样的,然而并不一样,虽然他们的表现方式都一样,都是多个方法一起执行。
  并行:并行表示多个方法同时执行。
  并发:并发表示在同一时间内只执行一个方法,但是多个方法之间是交替进行的,这个由操作系统进行调度。
如下图:

  对于单cpu来说是不会出现并行的程序,只能是并发,而多核cpu来说并行每个进程去执行一个方法。

临界区

  对于并发程序来说,临界区表示一种公共的资源或者是共享数据,可以被多个线程所使用,但是每一次只能有一个线程去使用它,一旦被占用则其他线程都必须等待。

阻塞(blocking)和非阻塞(Non-Blocking)

  阻塞与非阻塞通常是用来说线程之间的相互的影响。
  阻塞:如果一个线程占用了临界区,那么其他线程如果需要获取这个资源则必须在临界区中进行等待,等待就使得这个线程被挂起,这就是阻塞。但是如果占用临界区的资源的线程一直不释放资源,则其他需要获取资源的线程都将不能继续工作。
  非阻塞:非阻塞跟上述阻塞恰恰相反,它允许多个线程同时进入临界区,但是需要保证不能破坏临界区。

死锁(deadlock)、饥饿(starvation)和活锁(livelock)

  对于阻塞有来说,有可能是死锁,饥饿或者活锁造成。

  死锁:如果有3个方法a,b,c,有三个资源A,B,C,现在a方法占用了A资源,但是继续执行需要获取B资源,b方法占用了B资源,继续执行需要获取C资源,c方法占用了C资源,继续执行需要获取A资源,这样就会导致3个方法都没有办法继续执行,就会导致死锁,如果想要执行下去,就要先释放某个资源,比如不要c方法,将C资源释放,则b方法就会继续执行,b方法执行结束,释放了B资源,则a方法才可以继续执行下去。虽然死锁不是一个好现象,但是它属于一个静态问题,比较好找到原因。

  活锁:与死锁相对应。死锁是谁都想获取资源,活锁则恰恰相反,活锁是希望对方先获取资源,有方法a,b,资源A,B,a方法获取了A资源,执行时需要获取资源B,但是发现B字眼被b方法所占用于是就释放了A资源,而b方法则是占用了资源B,执行时发现资源A被占用,则释放了资源B,当资源都释放之后又再次去占用,反复如此则没法继续工作。如果想要继续执行,就要先让其中某个线程不进行资源释放,这样就不会出现阻塞了。活锁的话如果出现则比较难查找原因,因为它属于一个动态的死锁。

  饥饿:饥饿表示,如果一个线程的优先级比较低,与其他几个获取同一资源的线程去竞争资源时,资源总是被分配给优先级比较高的线程,则优先级比较低的线程就一直获取不到资源而被饿死。或者是,某个线程在做某个原子操作时总是失败,也会导致线程饿死。

并行级别

  并发分为4个级别,其中只有阻塞级别是阻塞类型的,其他三个级别无障碍,无锁,无等待都是非阻塞类型的。
  阻塞:当一个线程进入临界区后,其他线程必须等待。属于一种悲观状态,就是说,如果由多个线程去修改资源,肯定会把数据破坏,所以每次只能由一个线程去修改资源。

  无障碍:无障碍属于一种最弱的非阻塞调度,自由出入临界区。它属于一种比较乐观的状态,如果由多个线程去修改资源,未必会将数据破坏,当无竞争时正常执行,但是如果有数据竞争时则会回滚按户籍,重新进行操作。

  无锁:无锁也是无障碍的,前面无障碍的程序有可能多个线程之间竞争数据时互相影响,则会导致所有线程都需要重试,最后导致所有线程都无法继续执行下去,而无锁则会保证一定会有一个线程竞争胜利,这样就会让整个程序继续执行。

  无等待:无等待也是无锁的,它要求所有线程都必须在有限步内完成,所有它也是无饥饿的。无等待的程序就属于并发程序中最高的级别,他可以保证程序流畅就执行下去。比如所有线程都是读,而没有写,这样就不会导致资源被破坏,所有都可以在有限步内完成。

并行的2个定律

下面说两个与并行程序相关的2个定律

  • Amdahl定律(阿姆达尔定律)
    Amdahl定律定义了串行系统转换成并行系统加速比和理论的上限。
    加速比定义:加速吧=优化前系统耗时/优化后系统耗时
    我们看下面例子:

  • 如果一个程序执行需要5个步骤,其中每个步骤耗时为100,这样这个程序执行结束耗时为500.我们优化之后将其中两个步骤使用2个进程去跑其中每个步骤为50,这时耗时为400.

这时的 加速比=优化前系统耗时/优化后系统耗时=500/400=1.25
Amdahl定律有一个公式:

公式中各个字母的含义

  1. n 表示cpu的个数,上面例子中n = 2
  2. F 表示串行的比例,上面例子中F = 0.6
  3. 1-F 表示并行的比例,上面例子中 1-F = 0.4
  4. T1 表示单个cpu执行时的的耗时
  5. Tn 表示n个cpu执行时的耗时

    将上面公式进行带入。

    化简得:

最后得到一个结论,增加CPU处理器个数,并不一定可以起到提高系统内并行化模块的比重,合理的增加并行处理数量,才能以最小的投入,得到最大的加速比。

  • Gustafsson定律(古斯塔夫森定律)
    说明处理器个数,串行比例和加速比之间的关系。
    优化之后的执行时间:
    优化之前的执行时间:
    加速比:
    串行比例:
    加速比:


  1. a 表示串行时间
  2. b 表示并行时间
  3. n 表示cpu个数

上述公式说明,只要程序足够并行化,那么加速比和CPU个数成正比,也就是说程序足够并行化,CPU数量越多,加速比越高。

综合上述2个公式所得,程序的加速比和串行化比例和CPU个数相关。

-------------------- END ---------------------

 

 

 

最后附上作者的微信公众号地址和博客地址 

 

 

 

公众号:wuyouxin_gzh

 

 

 

 

 

 

 

 

 

 

Herrt灬凌夜:https://www.cnblogs.com/wuyx/

猜你喜欢

转载自www.cnblogs.com/wuyx/p/10231200.html