一、什么是异步编程
阻塞
- 程序未得到所需计算资源时被挂起的状态。
- 程序在等待某个操作完成期间,自身无法继续干别的事情,则称该程序在该操作上是阻塞的。
- 常见的阻塞形式有:网络I/O阻塞、磁盘I/O阻塞、用户输入阻塞等。
阻塞是无处不在的,包括CPU切换上下文时,所有的进程都无法真正干事情,它们也会被阻塞。(如果是多核CPU则正在执行上下文切换操作的核不可被利用。)
非阻塞
- 程序在等待某操作过程中,自身不被阻塞,可以继续运行干别的事情,则称该程序在该操作上是非阻塞的。
- 非阻塞并不是在任何程序级别、任何情况下都可以存在的。
- 仅当程序封装的级别可以囊括独立的子程序单元时,它才可能存在非阻塞状态。
非阻塞的存在是因为阻塞存在,正因为某个操作阻塞导致的耗时与效率低下,我们才要把它变成非阻塞的。
同步
- 不同程序单元为了完成某个任务,在执行过程中需靠某种通信方式以协调一致,称这些程序单元是同步执行的。
- 例如购物系统中更新商品库存,需要用“行锁”作为通信信号,让不同的更新请求强制排队顺序执行,那更新库存的操作是同步的。
- 简言之,同步意味着有序
异步
- 为完成某个任务,不同程序单元之间过程中无需通信协调,也能完成任务的方式。
- 不相关的程序单元之间可以是异步的。
- 例如,爬虫下载网页。调度程序调用下载程序后,即可调度其他任务,而无需与该下载任务保持通信以协调行为。不同网页的下载、保存等操作都是无关的,也无需相互通知协调。这些异步操作的完成时刻并不确定。
- 简言之,异步意味着无序。
上文提到的“通信方式”通常是指异步和并发编程提供的同步原语,如信号量、锁、同步队列等等。我们需知道,虽然这些通信方式是为了让多个程序在一定条件下同步执行,但正因为是异步的存在,才需要这些通信方式。如果所有程序都是按序执行,其本身就是同步的,又何需这些同步信号呢?
并发
- 并发描述的是程序的组织结构。指程序要被设计成多个可独立执行的子任务。
- 以利用有限的计算机资源使多个任务可以被实时或近实时执行为目的
并行
- 并行描述的是程序的执行状态。指多个任务同时被执行。
- 以利用富余计算资源(多核CPU)加速完成多个任务为目的
并发提供了一种程序组织结构方式,让问题的解决方案可以并行执行,但并行执行不是必须的
概念总结
- 并行是为了利用多核加速多任务完成的进度
- 并发是为了让独立的子任务都有机会被尽快执行,但不一定能加速整体进度
- 非阻塞是为了提高程序整体执行效率
- 异步是高效地组织非阻塞任务的方式
要支持并发,必须拆分为多任务,不同任务相对而言才有阻塞/非阻塞、同步/异步。所以,并发、异步、非阻塞三个词总是如影随形
异步编程
- 以进程、线程、协程、函数/方法作为执行任务程序的基本单位,结合回调、事件循环、信号量等机制,以提高程序整体执行效率和并发能力的编程方式
如果在某程序的运行时,能根据已经执行的指令准确判断它接下来要进行哪个具体操作,那它是同步程序,反之则为异步程序。(无序与有序的区别)
同步/异步、阻塞/非阻塞并非水火不容,要看讨论的程序所处的封装级别。例如购物程序在处理多个用户的浏览请求可以是异步的,而更新库存时必须是同步的
GIL 全局解释器锁
如何解决线程安全问题?CPython 解释器使用了加锁的方法。每个进程有一把锁,启动线程先加锁,结束线程释放锁。打个比方,进程是一个厂房,厂房大门是开着的,门内有锁,工人进入大门后可以在内部上锁。厂房里面有 10 个车间对应 10 个线程,每个 CPU 就是一个工人。GIL(Global Interpreter Lock)全局锁就相当于厂房规定:工人要到车间工作,从厂房大门进去后要在里面反锁,完成工作后开锁出门,下一个工人再进门上锁。也就是说,任意时刻厂房里只能有一个工人,但这样就保证了工作的安全性,这就是 GIL 的原理。当然了,GIL 的存在有很多其它益处,包括简化 CPython 解释器和大量扩展的实现
根据上面的例子可以看出 GIL 实现了线程操作的安全性,但多线程的效率被大打折扣,一个工厂里只能有一个工人干活,很难想象。这也是 David Beazley(《Python 参考手册》和《Python Cookbook》的作者)说 “Python 线程毫无用处” 的原因。
注意,GIL 不是语言特性,而是解释器的设计特点,有些 Python 解释器例如 JPython 就没有 GIL ,除了 Python 其它语言也有 GIL 设计,例如 Ruby
https://greasyfork.org/zh-CN
作者:dtdh
链接:https://www.jianshu.com/p/794c7887b0cb
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。