回顾
并行和并发
表示程序/计算机具有处理多个任务的能力
并行表示可以同时处理多个任务(几个多核CPU)
并发无法同时处理多个任务,但是可以基于时间片轮转法在多任务间快速切换的执行任务。
同步和异步
在基于并行或者并发处理任务的时候,任务中如果出现阻塞操作,就可以选择使用同步或者异步的方式进行处理阻塞操作。
同步处理:让CPU等待阻塞操作结束后,在继续执行处理阻塞后面的其他操作。
异步处理:遇到阻塞操作当前任务会交出CPU的使用权,让CPU可以继续处理其他任务。
因此,在异步应用的过程中,如果一个任务出现了阻塞操作,可以将阻塞操作单独封装成一个单独的任务。
进程和线程
是用来实现异步的两种技术手段
在异步应用的过程中,如果一个任务出现了阻塞操作,可以将阻塞操作单独封装成一个单独的任务,这个任务就是进程或者线程。
协程(重要!)
我们知道,无论是多进程还是多线程,在遇到IO阻塞时都会被操作系统强行剥夺走CPU的执行权限(使得cup执行其他操作,其他操作可能是我们程序的其他部分,也可能是其他的应用程序),我们自己程序的执行效率因此就降低了下来。
解决这一问题的关键在于:
我们自己从自己的应用程序级别检测到IO阻塞,然后使得cpu切换到我们自己程序的其他部分/任务执行(这里的任务指的是当前我们自己程序表示的进程或线程中的某一组操作/子程序),这样可以把我们程序的IO阻塞降到最低,我们的程序处于就绪态就会增多,以此来迷惑操作系统,操作系统便以为我们的程序是IO阻塞比较少的程序,从而会尽可能多的分配CPU给我们,这样也就达到了提升程序执行效率的目的。
通俗理解:
一个线程/进程可以表示一组指定行为的操作,这个操作可以由多个执行步骤组成,这些执行步骤有的是阻塞操作有的非阻塞操作,那么,当cpu执行当前进程/线程的时候遇到了阻塞的执行步骤的时候,如果不对其处理,则包含当前执行步骤的进程/线程就会被挂起进入到阻塞状态,且交出cpu的使用权(cpu就被别人抢走了)。那么如果遇到阻塞的执行步骤,我们的程序可以检测出它是阻塞的,且可以将cpu切换到我们自己程序其他非阻塞的执行步骤时,则包含这些执行步骤的进程/线程就不会进入到阻塞状态,从而减少进程/线程的阻塞状态,增加就绪状态(牢牢抢占cup)极大幅度提升程序执行的效率。
因此,有了协程后,在单进程或者单线程的模式下,就可以大幅度提升程序的运行效率了!
在python3.5之后新增了asyncio模块,可以帮我们检测IO(只能是网络IO【HTTP连接就是网络IO操作】),实现应用程序级别的切换(异步IO)。
接下来让我们来了解下协程的实现,从 Python 3.4 开始,Python 中加入了协程的概念,但这个版本的协程还是以生成器对象为基础的,在 Python 3.5 则增加了 asyncio,使得协程的实现更加方便。首先我们需要了解下面几个概念:
特殊函数:
在函数定义前添加一个async关键字,则该函数就变为了一个特殊的函数!
特殊函数的特殊之处是什么?
1.特殊函数被调用后,函数内部的程序语句(函数体)没有被立即执行
2.特殊函数被调用后,会返回一个协程对象
协程:
协程对象,特殊函数调用后就可以返回/创建了一个协程对象。
协程对象 == 特殊的函数 == 一组指定形式的操作
协程对象 == 一组指定形式的操作
任务:
任务对象就是一个高级的协程对象。高级之处,后面讲,不着急!
任务对象 == 协程对象 == 一组指定形式的操作
任务对象 == 一组指定形式的操作
事件循环:
事件循环对象(Event Loop),可以将其当做是一个容器,该容器是用来装载任务对象的。所以说,让创建好了一个或多个任务对象后,下一步就需要将任务对象全部装载在事件循环对象中。
思考:为什么需要将任务对象装载在事件循环对象?
当将任务对象装载在事件循环中后,启动事件循环对象,则其内部装载的任务对象对应的相关操作就会被立即执行。
import asyncio
import time
特殊的函数
async def get_request(url):
print('正在请求的网址是:',url)
time.sleep(2)
print('请求网址结束!')
return 123
创建了一个协程对象
c = get_request('www.1.com')
创建任务对象
task = asyncio.ensure_future(c)
创建事件循环对象
loop = asyncio.get_event_loop()
将任务对象装载在loop对象中且启动事件循环对象
loop.run_until_complete(task)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
任务对象对比协程对象的高级之处重点在于:
可以给任务对象绑定一个回调函数!
回调函数有什么作用?
回调函数就是回头调用的函数,因此要这么理解,当任务对象被执行结束后,会立即调用给任务对象绑定的这个回调函数!