Python异步编程
协程
- 参考网址
- https://www.cnblogs.com/wupeiqi/p/12834355.html
- 不是计算机提供,程序员人为创造
- 也称为微线程,是一种上下文切换技术(通过一个线程实现代码块互相切换执行)
- 普通代码的执行流程自上而下顺序执行
def fun1():
    print(1)
    # ...
    print(2)
def fun2():
    print(3)
    # ...
    print(4)
fun1()
fun2()
- 结果: 等fun1执行完,再执行fun2
- 
实现方法 - greenlet: 早期模块
- yield 关键字
- asyncio装饰器(py3.4开始支持)
- async,await关键字(py3.5开始支持,推荐)
 
- 
greenlet实现协程 
from greenlet import greenlet
def fun1():
    print(1)
    gr2.switch() # 跳转到fun2
    print(2)
    gr2.switch()
def fun2():
    print(3)
    gr1.switch() # 跳转到fun1
    print(4)
gr1 = greenlet(fun1)
gr2 = greenlet(fun2)
gr1.switch() # 开始执行
- 返回结果:
	1
    3
    2
    4
- yield示例
def fun1():
    yield 1
    yield from fun2() # 切换到 fun2
    yield 2
def fun2():
    yield 3
    yield 4
f1 = fun1()
for item in f1:
    print(item)
    
- 返回结果:
	1
    3
    4
    2
- asyncio示例
import asyncio
# 先定义两个协程对象
@asyncio.coroutine
def fun1():
    print(1)
    # 这里不能直接 asyncio.sleep(2)(报错信息: coroutine 'sleep' was never awaited)
    yield from asyncio.sleep(2)
    print(2)
    
@asyncio.coroutine
def fun2():
    print(3)
    yield from asyncio.sleep(2)
    print(4)
# 创建任务
tasks = [
    asyncio.ensure_future(fun1()),
    asyncio.ensure_future(fun2())
]
# 创建'轮询对象'并执行任务
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
- async & await示例
import asyncio
async def fun1(): # 协程函数对象
    print(1)
    await asyncio.sleep(2) # 等的时候切换到其他代码块并执行
    print(2)
async def fun2():
    print(3)
    await asyncio.sleep(2)
    print(4)
tasks = [
    asyncio.ensure_future(fun1()),
    asyncio.ensure_future(fun2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
- 结果:
	1
    3
    2
    4
- 非异步协程下载图片示例
import requests
url_list = [
    'https://www.aclas.com/uploadfile/uploadfile/225986195944098.png',
    'https://www.aclas.com/uploadfile/uploadfile/26507715014500.jpg',
    'https://www.aclas.com/uploadfile/uploadfile/34698673154118.jpg',
]
def download_image(url):
    filename = url.rsplit('/')[-1]
    res = requests.get(url)
    with open(filename, 'wb') as file:
        file.write(res.content)
    print(filename + '下载完成')
if __name__ == '__main__':
    for url in url_list:
        download_image(url)
- 异步协程下载图片示例
import aiohttp
import asyncio
url_list = [
    'https://www.aclas.com/uploadfile/uploadfile/225986195944098.png',
    'https://www.aclas.com/uploadfile/uploadfile/26507715014500.jpg',
    'https://www.aclas.com/uploadfile/uploadfile/34698673154118.jpg',
]
# 下载图片
async def download_image(request,url):
    print('开始发送请求',url)
    async with request.get(url,verify_ssl=False) as res:
        content = await res.content.read()
        filename = url.rsplit('/')[-1]
        with open(filename, 'wb') as file:
            file.write(content)
        print(filename + '下载完成')
        
# 创建请求对象并创建任务,等待任务执行完成
async def main():
    async with aiohttp.ClientSession() as request:
        tasks = [asyncio.create_task(download_image(request,url)) for url in url_list]
        await asyncio.wait(tasks)
    
    
if __name__ == '__main__':
    # asyncio.run(main()) 这种写法会报错...
    loop = asyncio.get_event_loop() # 创建'轮询对象'并开始执行
    loop.run_until_complete(main())
异步协程
- 参考网址
- https://www.cnblogs.com/wupeiqi/p/12834355.html
意义
- 计算型的操作,利用协程来回切换执行,没有任何意义,来回切换并保存状态 反倒会降低性能。
- IO型的操作,利用协程在IO等待时间就去切换执行其他任务,当IO操作结束后再自动回调,那么就会大大节省资源并提供性能,从而实现异步编程(不等待任务结束就可以去执行其他代码)。
底层原理,伪代码演示
- 事件循环,可以把他当做是一个while循环,这个while循环在周期性的运行并执行一些任务,在特定条件下终止循环
# 伪代码
任务列表 = [ 任务1, 任务2, 任务3,... ]
while True:
    可执行的任务列表,已完成的任务列表 = 去任务列表中检查所有的任务,将'可执行'和'已完成'的任务返回
    for 就绪任务 in 已准备就绪的任务列表:
        执行已就绪的任务
    for 已完成的任务 in 已完成的任务列表:
        在任务列表中移除 已完成的任务
    如果 任务列表 中的任务都已完成,则终止循环
开始
- 必须创建一个轮询对象
import asyncio
loop = asyncio.get_event_loop() # 创建轮询对象
- 定义协程函数
# 定义一个协程函数
async def func():
    pass
# 调用协程函数,返回一个协程对象
# 注意事项: 调用协程函数时,函数内部代码不会执行,只是会返回一个协程对象
result = func() 
- 基本应用: 事件循环和协程对象配合才能实现
import asyncio
async def func():
    print("协程内部代码")
# 调用协程函数,返回一个协程对象。
result = func()
# 方式一
# loop = asyncio.get_event_loop() # 创建一个事件循环
# loop.run_until_complete(result) # 将协程当做任务提交到事件循环的任务列表中,协程执行完成之后终止。
# 方式二
# 本质上方式一是一样的,内部先 创建事件循环 然后执行 run_until_complete,一个简便的写法。
# asyncio.run 函数在 Python 3.7 中加入 asyncio 模块,
asyncio.run(result)
- await关键字: 是一个只能在协程函数中使用的关键字
- 用于遇到IO操作时挂起 当前协程(任务),当前协程(任务)挂起过程中 事件循环可以去执行其他的协程(任务),当前协程IO处理完成时,可以再次切换回来执行await之后的代码
import asyncio
async def func():
    print("执行协程函数内部代码")
    # 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行。
    # 当前协程挂起时,事件循环可以去执行其他协程(任务)。
    response = await asyncio.sleep(2)
    print("IO请求结束,结果为:", response)
result = func()
asyncio.run(result)
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号