深入理解并发/并行,阻塞/非阻塞,同步/异步

1. 阻塞,非阻塞

首先,阻塞这个词来自操作系统的线程/进程的状态模型中,如下图:

一个线程/进程经历的5个状态,创建,就绪,运行,阻塞,终止。各个状态的转换条件如上图,其中有个阻塞状态,就是说当线程中调用某个函数,需要IO请求,或者暂时得不到竞争资源的,操作系统会把该线程阻塞起来,避免浪费CPU资源,等到得到了资源,再变成就绪状态,等待CPU调度运行。

阻塞调用是指调用结果返回之前,调用者会进入阻塞状态等待。只有在得到结果之后才会返回。

非阻塞调用是指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。

阻塞调用:比如 socket 的 recv(),调用这个函数的线程如果没有数据返回,它会一直阻塞着,也就是 recv() 后面的代码都不会执行了,程序就停在 recv() 这里等待,所以一般把 recv() 放在单独的线程里调用。

非阻塞调用:比如非阻塞socket 的 send(),调用这个函数,它只是把待发送的数据复制到TCP输出缓冲区中,就立刻返回了,线程并不会阻塞,数据有没有发出去 send() 是不知道的,不会等待它发出去才返回的。

拓展

如果线程始终阻塞着,永远得不到资源,于是就发生了死锁。

比如A线程要X,Y资源才能继续运行,B线程也要X,Y资源才能运行,但X,Y同时只能给一个线程用(即互斥条件)用的时候其他线程又不能抢夺。

A 有 X,等待 Y。
B 有 Y,等待 X。

于是A,B发生了循环等待,造成死锁。给用户的感觉就是程序卡着不动了。

在写代码的时候要特别注意共享资源的使用,用信号量控制好,避免造成死锁。死锁的解除有个著名的银行家算法

阻塞和挂起:阻塞是被动的,比如抢不到资源。挂起是主动的,线程自己调用 suspend() 把自己退出运行态了,某些时候调用 resume() 又恢复运行。

线程执行完就会被销毁,如果不想线程被频繁的创建,销毁,怎么办?可以给线程里面写个死循环,或者让线程有任务的时候执行,没任务的时候挂起,就像iOS中的 runloop 机制一样。线程就不会随便的终止了。


2. 同步,异步

同步:在发出一个同步调用时,在没有得到结果之前,该调用就不返回。

异步:在发出一个异步调用后,调用者不会立刻得到结果,该调用就返回了。

同步例子

int n = func();
next();
// func() 的结果没有返回,next() 就不会执行,直到 func() 运行完。

异步例子

func(callback);
next();
...

void callback(int n)     // func 结果回调
{
  int k = n;
}
// func() 执行后,还没得出结果就立即返回,然后执行 next() 了
// 等到结果出来,func() 回调 callback() 通知调用者结果。

同步的定义看起来跟阻塞很像,但是同步跟阻塞是两个概念,同步调用的时候,线程不一定阻塞,调用虽然没返回,但它还是在运行状态中的,CPU很可能还在执行这段代码,而阻塞的话,它就肯定不在CPU中跑这个代码了。这就是同步和阻塞的区别。同步是肯定可以在,阻塞是肯定不在。

异步和非阻塞的定义比较像,两者的区别是异步是说调用的时候结果不会马上返回,线程可能被阻塞起来,也可能不阻塞,两者没关系。非阻塞是说调用的时候,线程肯定不会进入阻塞状态。

上面两组概念,就有4种组合。

同步阻塞调用:得不到结果不返回,线程进入阻塞态等待。

同步非阻塞调用:得不到结果不返回,线程不阻塞一直在CPU运行。

异步阻塞调用:去到别的线程,让别的线程阻塞起来等待结果,自己不阻塞。

异步非阻塞调用:去到别的线程,别的线程一直在运行,直到得出结果。

3. 并发,并行

先从定义说起,定义经过我通俗化了,原定义有点难理解。

并发是指一个时间段内,有几个程序都在同一个CPU上运行,但任意一个时刻点上只有一个程序在处理机上运行。

并行是指一个时间段内,有几个程序都在几个CPU上运行,任意一个时刻点上,有多个程序在同时运行,并且多道程序之间互不干扰。 两者区别如下图

并行是多个程序在多个CPU上同时运行,任意一个时刻可以有很多个程序同时运行,互不干扰。

并发是多个程序在一个CPU上运行,CPU在多个程序之间快速切换,微观上不是同时运行,任意一个时刻只有一个程序在运行,但宏观上看起来就像多个程序同时运行一样,因为CPU切换速度非常快,时间片是64ms(每64ms切换一次,不同的操作系统有不同的时间),人类的反应速度是100ms,你还没反应过来,CPU已经切换了好几个程序了。

举个例子吧,并行就是,多个人,有人在扫地,有人在做饭,有人在洗衣服,扫地,做饭,洗衣服都是同时进行的。
并发就是,有一个人,这个人一会儿扫地,一会儿做饭,一会儿洗衣服,他在这3件事中来回做,同一时刻只做一件事,不是同时做的,但最后3件事都可以做完。

时间片大小的选取
时间片取的小,假设是20ms,切换耗时假设是 10ms。
那么用户感觉不到多个程序之间会卡,响应很快,因为切换太快了,但是CPU的利用率就低了,20 / (20 + 10) = 66% 只有这么多,33%都浪费了。

时间片取的大,假设是200ms,切换耗时是 10ms
那么用户会觉得程序卡,响应慢,因为要200ms后才轮到我的程序运行,但是CPU利用率就高了,200 / (200 + 10) = 95% 有这么多被利用的。

所以时间片取太大或者太小都不好,一般在 10 - 100 ms 之间。

CPU调度策略

在并发运行中,CPU需要在多个程序之间来回切换,那么如何切换就有一些策略

3.1 先来先服务 - 时间片轮转调度

这个很简单,就是谁先来,就给谁分配时间片运行,缺点是有些紧急的任务要很久才能得到运行。

3.2 优先级调度

每个线程有一个优先级,CPU每次去拿优先级高的运行,优先级低的等等,为了避免等太久,每等一定时间,就给线程提高一个优先级

3.3 最短作业优先

把线程任务量排序,每次拿处理时间短的线程运行,就像我去银行办业务一样,我的事情很快就处理完了,所以让我插队先办完,后面时间长的人先等等,时间长的人就很难得到响应了。

3.4 最高响应比优先

用线程的等待时间除以服务时间,得到响应比,响应比小的优先运行。这样不会造成某些任务一直得不到响应。

3.5 多级反馈队列调度

有多个优先级不同的队列,每个队列里面有多个等待线程。
CPU每次从优先级高的遍历到低的,取队首的线程运行,运行完了放回队尾,优先级越高,时间片越短,即响应越快,时间片就不是固定的了。
队列内部还是用先来先服务的策略。


时间: 2024-10-03 02:47:14

深入理解并发/并行,阻塞/非阻塞,同步/异步的相关文章

怎样理解阻塞非阻塞与同步异步的区别?

著作权归作者所有. 商业转载请联系作者获得授权,非商业转载请注明出处. 作者:卢毅 链接:http://www.zhihu.com/question/19732473/answer/20851256 来源:知乎 "阻塞"与"非阻塞"与"同步"与"异步"不能简单的从字面理解,提供一个从分布式系统角度的回答.1.同步与异步 同步和异步关注的是消息通信机制 (synchronous communication/ asynchrono

同步/异步 阻塞/非阻塞 .

http://blog.csdn.net/lb85858585/article/details/6693458  同步/异步与阻塞/非阻塞的区别 我喜欢用自己的语言通过联系现实生活中的一些现象解释一些概念,当我能做到这一点时,说明我已经理解了这个概念.今天要解释的概念是:同步/异步与阻塞/非阻塞的区别. 这两组概念常常让人迷惑,因为它们都是涉及到IO处理,同时又有着一些相类似的地方. 首先来解释同步和异步的概念,这两个概念与消息的通知机制有关. 举个例子,比如我去银行办理业务,可能选择排队等候,

[转]举例说同步异步阻塞非阻塞机制

劫匪冲进某花园小别墅,端着AK47,向某衰男甩过去一个袋子:说,快,把现金拿出来给我放进去,这个时候劫匪一直跟着衰男去装钱,看钱装满了他就拿走,此时劫匪是阻塞 的,因为劫匪不能干其他的,得看着这个衰男,免得他搞鬼.    想象下,如果劫匪等衰男自己去装钱,在钱装满之前,他自己可以到处翻箱倒柜,找点值钱的首饰黄金之类的,那么这个时候劫匪就是非阻塞 的,因为劫匪并没有阻塞在衰男去装钱这个环节上,劫匪可以自由活动做点其他的小买卖.       再来回顾下上面的情节,如果劫匪在抢*劫时需要不时自己看看衰

Windows I/O模型、同步/异步、阻塞/非阻塞

同步 异步 异步的概念和同步相对.当一个异步过程调用发出后,调用者不能立刻得到结果.实际处理这个调用的部件在完成后,通过状态.通知和回调来通知调用者.以 CAsycSocket类为例(注意,CSocket从CAsyncSocket派生,但是起功能已经由异步转化为同步),当一个客户端通过调用 Connect函数发出一个连接请求后,调用者线程立刻可以朝下运行.当连接真正建立起来以后,socket底层会发送一个消息通知该对象.这里提到执行 部件和调用者通过三种途径返回结果:状态.通知和回调.可以使用哪

Linux驱动技术(五) _设备阻塞/非阻塞读写

等待队列是内核中实现进程调度的一个十分重要的数据结构,其任务是维护一个链表,链表中每一个节点都是一个PCB(进程控制块),内核会将PCB挂在等待队列中的所有进程都调度为睡眠状态,直到某个唤醒的条件发生.应用层的阻塞IO与非阻塞IO的使用我已经在Linux I/O多路复用一文中讨论过了,本文主要讨论驱动中怎么实现对设备IO的阻塞与非阻塞读写.显然,实现这种与阻塞相关的机制要用到等待队列机制.本文的内核源码使用的是3.14.0版本 设备阻塞IO的实现 当我们读写设备文件的IO时,最终会回调驱动中相应

PHP-Socket-阻塞与非阻塞,同步与异步概念的理解

1. 概念理解        在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式:同步:       所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.也就是必须一件一件事做,等前一件做完了才能做下一件事.       例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事   异步:       异步的概念和同步相对.当一个异步过程调用发出后,调用

关于网络IO中的同步、异步、阻塞、非阻塞

在高并发编程当中,我们经常会遇到一些异步.非阻塞等一些概念,一些常用的技术比如异步的httpclient.netty nio.nginx.node.js等,它们的原理大都跟异步.非阻塞有关.特别是在服务器开发中,并发的请求处理是个大问题,阻塞式的函数会导致资源浪费和时间延迟.通过事件注册.异步函数,开发人员可以提高资源的利用率,性能也会改善.其nginx和node.js处理并发都是采用的事件驱动异步非阻塞模式.其中nginx中处理并发用的是epoll,poll,queue等方式,node.js使

同步、异步、阻塞和非阻塞的概念

在进行网络编程时,我们常常见到同步.异步.阻塞和非阻塞四种调用方式.这些方式彼此概念并不好理解.下面是我对这些术语的理解.    同步         所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等).但是一般而言,我们在说同步.异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务.最常见的例子就是 SendMessage.该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返

也谈阻塞、非阻塞、同步、异步

最近在招聘中,发现不少人对BIO.NIO.AIO等理解非常模糊,觉得有必要写文章来纠正下很多人的误解. 在谈这些之前,非常有必要先介绍下Unix 5种IO模型: 阻塞: 阻塞是最常用的IO模型,默认情况下所有的文件操作都是阻塞的.以套接字编程为例.在进程空间中调用recvfrom,其系统调用直到数据报文到达且被拷贝到应用程序进程的缓存区(或者发生错误)后才返回,期间一直在等待.进程在从调用recvfrom开始到它返回的整段时间内是被阻塞的.有一张很经典的图: 非阻塞: 调用recvfrom从应用