并行

*进程是资源分配的最小单位,线程是 CPU 调度的最小单位*
为什么要并行?
*业务要求
*HTTP服务器要处理多个客户端的请求,通俗的做法是对每个客户端请求使用一个线程去做,也可以用一个线程处理多个客户端请求,但要注意调度客户端的问题;
*业务上需要一个执行单元,为了处理这个执行单元,就想到了线程;为什么不用进程?进程太大,一个进程创建和消耗的开销要比线程高的多,所以选择更低开销的实体-线程

*性能
*使用多线程程序在多核CPU上的性能上一般要比单线程程序好一些,对于并行程序来说,有弊有利;



*并行计算的代码要比串行难维护的多



*多核CPU兴起缘由:
*单核CPU发展受限,上限4GHZ++,大部分停留在3.8G,4G以下,人们想到单核CPU无法提升频率,但可以横向发展,向CPU中加核,于是出现了多核CPU,并行的前提。

*并行计算出于业务模型的需要
*并不是为了提高系统性能,而是确实在业务上需要多个执行单元。
*比如HTTP服务器,为每一个Socket连接新建一个处理线程
*让不同线程承担不同的业务工作
*简化任务调度

*有关并行的几个重要概念:
*同步(synchronous)和异步(asynchronous)
*并发(Concurrency)和并行(Parallelism)
*临界区
*阻塞(Blocking)和非阻塞(Non-Blocking)
*死锁(Deadlock)、饥饿(Starvation)、和活锁(Livelock)
*并行的级别


*1.同步(synchronous)和异步(asynchronous)
*同步调用会等待方法返回,方法执行多久就等待多久
*异步调用会瞬间返回,并不表示请求完成,一般会在后台起个线程,慢慢的做它的事情;异步调用之后,还会做之后的事情,不会影响后面的操作

*2.并发(Concurrency)和并行(Parallelism)
*并行:两个线程同时执行(单CPU不可能并行)
*并发:一会做一件事情,有一个调度的过程

*3.临界区(重要概念)
                    *临界区:表示一种公共资源或者说是共享数据,可以被多个线程使用。但是每一次,只能有一个线程使用它,一旦临界区资源被占用,其他线程想要使用这个资源,就必须等待。
*所有的线程都可以访问它,当多个线程访问的时候,可能会破坏掉;比如:一个人写了一些数据没写完,另一个人又来写了数据,导致数据的叠加,可能最终会产生错误的数据;因此临界区是需要控制的区域,控制每次只有一个线程可以进去,如果其他进程想进来,就要在阻塞队列进行等待,等到临界区内的线程释放了这个锁,其他才可以取一个线程进入临界区。额外关注,不被破坏

*4.阻塞(Blocking)和非阻塞(Non-Blocking)

*阻塞和非阻塞通常用来形容多线程间的相互影响。比如一个线程占用了临界区资源,那么其它所有需要这个资源的线程就必须在这个临界区中进行等待,等待会导致线程挂起。这种情况就是阻塞。此时,如果占用资源的线程一直不愿意释放资源,那么其它所有阻塞在这个临界区上的线程都不能工作。

*性能不会太好,线程如果在操作系统层面挂起来了,做了上下文切换了,通常需要8万个时常周期来做这个事情,不是特别好的方法,但是特别简单的方法,把所有的都推给了操作系统,虽然性能不高,但能做到很好,代价就是8万个时常周期

*非阻塞允许多个线程同时进入临界区 保证不把数据改坏就可以了

*5.死锁(Deadlock)、饥饿(Starvation)、和活锁(Livelock)
*关于阻塞的程序来讲,有可能会发生死锁的现象

*死锁:抢占了资源不释放
*死锁是静态的问题,不会占用cpu,较容易发现
*活锁:资源在多个线程中跳来跳去
*比如:线程A,线程B,同时需要资源C,资源D,A抢到了C,B抢到了D,A看抢不到D,就释放了自己的C,B看抢不到C,就释放了自己的D,而后A又发现有空资源CD,B也发现,于是就如此反复。
*活锁是动态的问题,不容易发现

*饥饿
*两个线程,一个线程优先级高,另一个低,总是调用优先级高的,另一个低的就会被饿死
*资源竞争,竞争数据,总是失败,可能会被饿死

*6.并行的级别


*阻塞:当一个线程进入临界区后,其他线程必须等待
* 无障碍(Obstruction-Free):宽进严出的策略
-- 无障碍是一种最弱的非阻塞调度
-- 自由出入临界区
-- 无竞争时,有限步内完成操作
-- 有竞争时,回滚数据
* 无锁(Lock-Free) 应用多
-- 是无障碍的
-- 保证有一个线程可以胜出
典型无锁计算代码:
while(!atomicVar. compareAndSet(localVar,localVar+1))
{
localVar = atomicVar.get();
}
*无等待(Wait-Free) 让系统发挥到组好的效率
-- 无锁的
-- 要求所有的线程都必须在有限步内完成
-- 无饥饿的

典型案例:相对麻烦,技巧性较多
读写线程
读线程没有写线程,所有的读线程都是无等待的
但是如果有一个写在里面,会导致读线程不是无等待的,可以提出一种算法做出改进,做一个算法:在每次写之前数据都拿出来复制一份,拿到副本,修改副本,不修改原始数据,修改的过程也不影响线程读,所有的读线程一样可以是无等待的,都可以在有限步内完成。
写线程,相对来讲,每个线程都是写自己的副本,也是无等待的,都不需要做彼此同步,只需要在写完后覆盖原始数据,覆盖原始数据是非常快的,只不过是指针或者引用做个替换而已,不管哪个写线程胜出,都能保证替换上去的数据是一致的,不会写坏,因为写的都是副本,只是一个指针指向谁的问题,数据是安全的


*有关并行的两个重要定律
*Amdahl定律(阿姆达尔定律)
当我们把一个程序改造成并行程序或者一个程序编程多个CPU去执行的时候,会有多快?


*Gustafson定律(古斯塔夫森)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值