基础概念
- 阻塞:(阻塞状态指程序未得到所需计算资源时被挂起的状态)程序在等待某个操作完成期间,自身无法继续干别的事情
- 非阻塞:程序在等待某个操作的过程中,自身不被阻塞,可以继续干别的事情(仅当程序封装的级别可以囊括独立的子程序单元时,程序才可能存在非阻塞状态)
- 同步:为了共同完成某个任务,不同程序单元之间在执行过程中需要靠某种通信方式保持协调一致,此时这些程序单元是同步执行的(有序的)
- 异步:为了完成某个任务,不同程序单元之间无须通信协调,此时不相关的程序单元之间可以是异步的(无序的)
- 多进程:利用多核CPU的优势,同一时间执行多个任务
- 协程(coroutine):本质上是个单进程,但拥有自己的寄存器上下文和栈,可以用来实现异步操作。
协程的用法
环境:Python 3.5+、asyncio库
基础概念
- event_loop:事件循环,可以把函数注册到这个事件循环上,当满足发生条件时,就调用对应的处理方法;
- coroutine(协程):指代协程对象类型。用async关键字定义一个方法,这个方法在调用时不会被立即执行,而是会返回一个协程对象。然后可以将协程对象注册到事件循环中,它就会被事件循环调用。
- task:可以对协程对象进一步封装,返回的task对象会包含运行状态信息。
- future:定义task对象的另一种方式,和task没有本质区别。
定义协程(直接将协程对象注册到事件循环中)
import asyncio
# 导入asyncio包,这样才能使用async和await关键字
async def execute(x):
print('Number:', x)
# 用async定义一个方法,该方法接收一个参数,执行之后就会打印这个参数
coroutine = execute(1)
print('Coroutine:', coroutine)
print('After calling execute')
# 直接调用前面定义的方法,但这个方法不会被执行,而是返回一个协程对象
loop = asyncio.get_event_loop()
# 创建一个事件循环loop对象
loop.run_until_complete(coroutine)
# 调用loop对象的run_until_complete方法将协程对象注册到事件循环中
# 注册完成之后,前面定义并调用的方法才会被执行
print('After calling loop')
Coroutine: <coroutine object execute at 0x000001E7D19743C0>
After calling execute
Number: 1
After calling loop
async关键字:async定义的方法会变成一个无法直接执行的协程对象,必须将此对象注册到事件循环中才可以执行!
定义协程(先用create_task方法将协程封装成task对象)
import asyncio
async def execute(x):
print('Number:', x)
return x
coroutine = execute(1)
print('Coroutine:', coroutine)
print('After calling execute')
loop = asyncio.get_event_loop()
# 创建一个事件循环loop对象
task = loop.create_task(coroutine)
# 调用loop对象的create_task方法将协程对象转化成task对象
print('Task:', task)
# 此时打印输出的task对象处于pending(待定)状态
loop.run_until_complete(task)
# 调用loop对象的run_until_complete方法将协程对象注册到事件循环中
print('Task', task)
# 此时打印输出的task对象处于finished状态
print('After calling loop')
Coroutine: <coroutine object execute at 0x000001EB521D5440>
After calling execute
Task: <Task pending name='Task-1' coro=<execute() running at D:\Python\demo\临时测试2.py:125>>
Number: 1
Task <Task finished name='Task-1' coro=<execute() done, defined at D:\Python\demo\临时测试2.py:125> result=1>
After calling loop
定义协程(先用ensure_future方法将协程封装成task对象)
import asyncio
async def execute(x):
print('Number:', x)
return x
coroutine = execute(1)
print('Coroutine:', coroutine)
print('After calling execute')
task = asyncio.ensure_future(coroutine)
# 直接调用asyncio包的ensure_future方法将协程对象转化成task对象(不需要先声明loop对象了)
print('Task:', task)
# 此时打印输出的task对象处于pending(待定)状态
loop = asyncio.get_event_loop()
# 创建一个事件循环loop对象
loop.run_until_complete(task)
# 调用loop对象的run_until_complete方法将协程对象注册到事件循环中
print('Task', task)
# 此时打印输出的task对象处于finished状态
print('After calling loop')
Coroutine: <coroutine object execute at 0x0000023C80885440>
After calling execute
Task: <Task pending name='Task-1' coro=<execute() running at D:\Python\demo\临时测试2.py:125>>
Number: 1
Task <Task finished name='Task-1' coro=<execute() done, defined at D:\Python\demo\临时测试2.py:125> result=1>
After calling loop
给task对象绑定一个回调方法(add_done_callback)
import asyncio
import requests
async def request():
url = 'https://www.baidu.com'
status = requests.get(url)
return status
# 用async定义一个方法,该方法会请求网站获取并返回状态码
def callback(task):
print('Status:', task.result())
# 正常定义一个callback方法,该方法会调用result方法打印出task对象的结果
coroutine = request()
# 接收返回的协程对象
task = asyncio.ensure_future(coroutine)
# 将协程对象封装成一个task对象
task.add_done_callback(callback)
# 给task对象指定一个回调方法
print('Task:', task)
# 打印这个task对象(其中包含运行状态信息,此时为:pending)
loop = asyncio.get_event_loop()
# 创建一个事件循环loop对象
loop.run_until_complete(task)
# 调用loop对象的方法,将task对象注册到事件循环中(注册完,前面定义的方法立刻被执行)
print('Task:', task)
# 再次打印这个task对象(此时的运行状态:finished)
Task: <Task pending name='Task-1' coro=<request() running at D:\Python\demo\临时测试2.py:148> cb=[callback() at D:\Python\demo\临时测试2.py:154]>
Status: <Response [200]>
Task: <Task finished name='Task-1' coro=<request() done, defined at D:\Python\demo\临时测试2.py:148> result=<Response [200]>>
实际上,在这个例子中,即使不使用回调方法,在task运行完毕后,也可以直接调用result方法获取结果:
import asyncio
import requests
async def request():
url = 'https://www.baidu.com'
status = requests.get(url)
return status
# 用async定义一个方法,该方法会请求网站获取并返回状态码
coroutine = request()
# 接收返回的协程对象
task = asyncio.ensure_future(coroutine)
# 将协程对象封装成一个task对象
print('Task:', task)
# 打印这个task对象(其中包含运行状态信息,此时为:pending)