协程 & asyncio & 异步
1. 协程 (coroutine)
协程不是计算机提供,而是程序员人为创造。
协程(coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块互相切换运行。例如:
def func1():
print(1)
...
print(2)
def func2():
print(3)
...
print(4)
func1()
func2()
实现协程有这么几种方法:
1.1 greenlet
实现协程
pip3 install greenlet
from greenlet import greenlet
def func1():
print(1) # 第 2 步:输出 1
gr2.switch() # 第 3 步:切换到 func2 函数
print(2) # 第 6 步:输出 2
gr2.switch() # 第 7 步 切换到 func2 函数,从上一次执行的位置继续向后执行
def func2():
print(3) # 第 4 步:输出 3
gr1.switch() # 第 5 步:切换到 func1 函数,从上一次执行的位置继续向后执行
print(4) # 第 8 步:输出 4
gr1 = greenlet(func1)
gr2 = greenlet(func2)
gr1.switch() # 第 1 步:去执行 func1 函数
1.2 yield
关键字
def func1():
yield 1
yield from func2()
yield 2
def func2():
yield 3
yield 4
f1 = func1()
for item in f1:
print(item)
伪实现,仅能实现协程的功能。
1.3 asyncio
在 python 3.4 及之后的版本。
import asyncio
@asyncio.coroutine
def func1():
print(1)
yield from asyncio.sleep(2) # 遇到 IO 耗时操作,自动化切换到 tasks 中其它任务
print(2)
@asyncio.coroutine
def func2():
print(3)
yield from asyncio.sleep(2) # 遇到 IO 耗时操作,自动化切换到 tasks 中其它任务
print(4)
tasks = [
asyncio.ensure_future(func1())
asyncio.ensure_future(func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
注意:遇到 IO 阻塞自动切换。
1.4 aynsc
& await
关键字
在 python 3.5 及之后的版本。
import asyncio
async def func1():
print(1)
# 网络 IO 请求:下载一张图片
await asyncio.sleep(2) # 遇到 IO 耗时操作,自动化切换到 tasks 中的其它任务。
print(2)
async def func2():
print(3)
# 网络 IO 请求:下载一张图片
await asyncio.sleep(2) # 遇到 IO 耗时操作,自动化切换到 tasks 中的其它任务。
print(4)
tasks = [
asyncio.ensure_future(func1())
asyncio.ensure_future(func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
2. 协程的意义
在一个线程中如果遇到 IO 等待时间,线程不会傻等,而是利用空闲时间再去干点其它事情。
案例:下载三张图片(网络 IO):
-
普通方式(同步)
pip3 install requests
import requests def download_image(url): print("开始下载:", url) response = requests.get(url) print("下载完成") file_name = url.rsplit("_")[-1] with open(file_name, mode="wb") as file_object: file_object.write(response.content) url_list = [ "https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar__ChsEe12AXQ6AOOH_AAFocMs8nzU621.jpg", "https://www3.autoimg.cn/newsdfs/g30/M01/3C/E2/120x90_0_autohomecar__ChcCSV2BBICAUntfAADjJFd6800429.jpg", "https://www3.autoimg.cn/newsdfs/g26/M0B/3C/65/120x90_0_autohomecar__ChcCP12BFCmAIO83AAGq7vk0sGY913.jpg", ] for item in url_list: download_image(item)
-
协程方式(异步)
pip3 install aiohttp
import aiohttp import asyncio async def fetch(session, url): print("发送请求:", url) async with session.get(url, verify_ssl=False) as response: content = await response.content.read() file_name = url.rsplit("_")[-1] with open(file_name, mode="wb") as file_object: file_object.write(content) async def main(): async with aiohttp.ClientSession() as session: url_list = [ "https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar__ChsEe12AXQ6AOOH_AAFocMs8nzU621.jpg", "https://www3.autoimg.cn/newsdfs/g30/M01/3C/E2/120x90_0_autohomecar__ChcCSV2BBICAUntfAADjJFd6800429.jpg", &qu