标准方法:
def f(x):
return 2*x
异步方法:
async def f(x):
return 2 *x
1.协程(不是计算机提供,程序员人为的)
也可以被称为微线程,是一种用户东来内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相执行。
例如
实现协程的方法:
- 1. greenlet,早期模块
- 2. yield关键字
- 3.asyncio装饰器(py.3.4)
- 4.async , await关键字(py.3.5)[推荐]
asyncio
import asyncio
@asyncio.coroutine
def func1():
print(1)
yield from asyncio.sleep(2)
print(2)
@asyncio.coroutine
def func2():
print(3)
yield from asyncio.sleep(2)
print(4)
tasks = [
asyncio.ensure_future( func1()),
asyncio.ensure_future( func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
4.python3.5后
import asyncio
async def func1():
print(1)
await asyncio.sleep(2)
print(2)
async def func2():
print(3)
await asyncio.sleep(2)
print(4)
tasks = [
asyncio.ensure_future( func1()),
asyncio.ensure_future( func2())
] #封装两个函数
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks)) #两个函数运行可以随意切换,当这个函数awit等待了,就随机切换到下一个函数
2.协程意义:
在一个线程中如果遇到IO等待时间,线程不会傻傻等待,会切换到其它程序执行
#普通下载图片,每一张图片下载完成了再开启下一张图片的下载,遇到了io等待时间的话,耗时较长
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)
if __name__ == '__main__':
url_list = [
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603213266343&di=b9712f1b1b4b4dacd33b2dec1effa452&imgtype=0&src=http%3A%2F%2Fa0.att.hudong.com%2F56%2F12%2F01300000164151121576126282411.jpg"
,
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603213266343&di=58d3e8cd434a928b3aea7271e0b1248a&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F14%2F75%2F01300000164186121366756803686.jpg"
,
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603213266342&di=6147a20ddadd13ba45fa1bb9d4acb064&imgtype=0&src=http%3A%2F%2Fa0.att.hudong.com%2F70%2F91%2F01300000261284122542917592865.jpg"
]
for i in url_list:
download_image(i)
协程方式下载
先发送每一个下载图片的请求,不用等每张图下载好在开始下一个,遇到io等待时间线程就发送图片请求,耗时较短
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(response.content)
print("下载完成", url)
async def main():
async with aiohttp.ClientSession() as session:
url_list = [
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603213266343&di=b9712f1b1b4b4dacd33b2dec1effa452&imgtype=0&src=http%3A%2F%2Fa0.att.hudong.com%2F56%2F12%2F01300000164151121576126282411.jpg"
,
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603213266343&di=58d3e8cd434a928b3aea7271e0b1248a&imgtype=0&src=http%3A%2F%2Fa3.att.hudong.com%2F14%2F75%2F01300000164186121366756803686.jpg"
,
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603213266342&di=6147a20ddadd13ba45fa1bb9d4acb064&imgtype=0&src=http%3A%2F%2Fa0.att.hudong.com%2F70%2F91%2F01300000261284122542917592865.jpg"
]
tasks = [ asyncio.create_task(fetch(session,url)) for url in url_list ]
await asyncio.wait(tasks)
if __name__ =='__main__':
asyncio.run( main())
3.异步编程
1.事件循环
import asyncio
#去生成或获取一个事件循环
loop = asyncio.get_event_loop()
#将任务放到”人物列表"
loop.run_until_complete(任务)
2.快速上手
import asyncio
async def func():
print("test test1")
result = func()
# loop = asyncio.get_event_loop()
# loop.run_until_complete(result)
asyncio.run(result) #python3.7后的方式
3.await + 可等待对象(协程对象,Future,Task对象->IO等待)
实例一.
import asyncio
async def func():
print("test!")
response = await asyncio.sleep(2) #在等待的时间切换去执行其他任务
print("结束",response)
asyncio.run( func())
实例二.
import asyncio
async def others():
print("start")
await asyncio.sleep(2)
print("end")
return "返回值"
async def func():
print("执行协程函数内部代码")
#遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行
#当前协程挂起时,事件循环可以去执行其他任务
response1 = await others() #等待对象的值得到结果之后再继续往下走
print("IO请求结束, 结果为:", response1)
response2 = await others()
print("IO请求结束, 结果为:", response1)
asyncio.run( func())
执行协程函数内部代码
start
end
IO请求结束, 结果为: 返回值
start
end
IO请求结束, 结果为: 返回值
4. Task对象
Tasks用于并发调度协程, 通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环
python3.7之前可以用loop.create_task()或ensure_future()函数.
import asyncio
from asyncio.tasks import Task
from typing import List, Any
async def others():
print("start")
await asyncio.sleep(2)
print("end")
return "返回值"
async def func():
print("func开始")
task_list: list[Task[Any]] = [
asyncio.create_task(others()),
asyncio.create_task(others())
]
print("func结束")
#当执行某协程遇到IO操作时,会自动切换执行其他任务。
#此处的await是等待相应的协程全部执行完毕并获取结果
ret1, ret2 = await asyncio.wait(task_list, timeout = None)
print(ret1, ret2)
asyncio.run( func())
5. Future对象
Task继承Future,Task对象内部await结果的处理基于Future对象来的.
import asyncio
async def set_after(fut):
await asyncio.sleep(2)
fut.set_result("666")
async def main():
#获取当前事件循环
loop = asyncio.get_running_loop()
#创建一个任务(future对象)
fut = loop.create_future()
#创建一个任务(task对象),绑定了set_after函数,函数内部在2s之后,会给fut赋值
#即手动设置future任务的最终结果,那么fut就可以结束了
await loop.create_task(set_after(fut))
#等待Future对象获取,最终结果,否则一直等下去
data = await fut
print(data)
asyncio.run(main())