Python异步编程

发布时间 2023-07-12 14:45:58作者: 清安宁

协程

  • 不是计算机提供,程序员人为创造
  • 也称为微线程,是一种上下文切换技术(通过一个线程实现代码块互相切换执行)
  • 普通代码的执行流程自上而下顺序执行
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())