python 异步编程(一)协程
概念
- 协程,也可以称作微线程,是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块互相切换
- 协程不是计算机提供,是程序员认为创造的
- 在一个线程中,如果遇到耗时 IO ,使线程利用等待时间干点别的事
实现
greenlet
比较早期的模块,用的人还是比较多的
from greenlet import greenlet
def func1():
print(1) # 第1步:输出 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
if __name__ == '__main__':
gr1 = greenlet(func1)
gr2 = greenlet(func2)
gr1.switch() # 第1步:去执行 func1 函数
yield
生成器关键字来实现协程(伪协程)
def func1():
yield 1
yield from func2()
yield 2
def func2():
yield 3
yield 4
if __name__ == '__main__':
for i in func1():
print(i)
asyncio
python 3.4 及更高版本可用
import asyncio
@asyncio.coroutines
def func1():
print(1)
yield from asyncio.sleep(2) # 固定写法,执行到该语句时,阻塞2秒去执行其它协程函数
print(2)
@asyncio.coroutines
def func2():
print(3)
yield from asyncio.sleep(2)
print(4)
if __name__ == '__main__':
# 以下为固定写法
tasks = [
asyncio.ensure_future( func1() ),
asyncio.ensure_future( func2() )
]
loop = asyncio.get_event_loop()
loop.run_until_complete( asyncio.wait(tasks) )
async & await
python 3.5 及更高版本可用 ,是目前最的主流方式
import asyncio
async def func1():
print(1)
await asyncio.sleep(2) # 固定写法,执行到该语句时,阻塞2秒去执行其它协程函数
print(2)
async def func2():
print(3)
await asyncio.sleep(2)
print(4)
if __name__ == '__main__':
# 以下为固定写法
tasks = [
asyncio.ensure_future( func1() ),
asyncio.ensure_future( func2() )
]
loop = asyncio.get_event_loop()
loop.run_until_complete( asyncio.wait(tasks) )
案例:下载图片
普通写法
import requests
def download_image(url):
print("开始下载:", url)
# 发送网络请求,下朝图片
response = requests.get(url)
print("下载完成")
# 图片保存到本地文件
file_name = url.rsplit('/')[-1]
with open('images/' + file_name, mode='wb') as file_object:
file_object.write(response.content)
if __name__ == '__main__':
url_list = [
"https://th.wallhaven.cc/small/pk/pkw6y3.jpg",
"https://th.wallhaven.cc/lg/dp/dpo38l.jpg",
"https://th.wallhaven.cc/small/8o/8oky1j.jpg"
]
for item in url_list:
download_image(item)
协程写法(异步)
requests库是一个同步请求库,使用异步请求可使用aiohttp库
import asyncio
import aiohttp
async def download_image(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('images/' + file_name, mode='wb') as file_object:
file_object.write(content)
print('下载完成' + url)
async def main():
async with aiohttp.ClientSession() as session:
url_list = [
"https://th.wallhaven.cc/small/pk/pkw6y3.jpg",
"https://th.wallhaven.cc/lg/dp/dpo38l.jpg",
"https://th.wallhaven.cc/small/8o/8oky1j.jpg"
]
tasks = [asyncio.create_task(download_image(session, url)) for url in url_list]
await asyncio.wait(tasks)
if __name__ == '__main__':
asyncio.run(main())