python 协程 & 异步编程(asyncio)

asyncio

协程

协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相互切换执行
协程意义:在一个线程中如果遇到IO等待时间,线程不会傻傻等,利用空闲的时候再去干点其他事。

一、效果演示

1.1 正常执行

from time import sleep
from time import time
start = time()
def func1():
    print(1)
    sleep(1)
    print(2)


def func2():
    print(3)
    sleep(1)
    print(4)
func1()
func2()
end = time()  # 输出1234
print(end-start) # 2.028127908706665

1.2 使用asynico

import asyncio
from time import time

start = time()
async def func1():
    print(1)
    await asyncio.sleep(1)
    print(2)

async def func2():
    print(3)
    await asyncio.sleep(1)
    print(4)

tasks = [
    func1(),
    func2()
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
end = time() # 输出 1324
print(end-start) # 1.010634422302246

可以很明显的看到程序在遇到i0费时操作时,asyncio的效率有多高

学习使用

3.1 事件循环

事件循环就是把你创建好的函数,加入到任务列表,事件循环负责监听任务列表,当执行这个函数遇到费时操作时,事件循环就会控制跳到其他可执行的任务,当没有可执行的任务就会等待当前任务执行完毕,只有全部任务都执行完毕,才会退出循环。

# 伪代码

任务列表 = [ 任务1, 任务2, 任务3,... ]

while True:
    可执行的任务列表,已完成的任务列表 = 去任务列表中检查所有的任务,将'可执行'和'已完成'的任务返回
    
    for 就绪任务 in 可执行的任务列表:
        执行已就绪的任务
        
    for 已完成的任务 in 已完成的任务列表:
        在任务列表中移除 已完成的任务

	如果 任务列表 中的任务都已完成,则终止循环
import asyncio

# 去生成或获取一个事件循环
loop = asyncio.get_event_loop()

# 将任务放到`任务列表`
loop.run_until_complete(任务)

asyncio.wait()

控制任务
通过asyncio.wait()可以控制多任务

asyncio.wait()是一个协程,不会阻塞,立即返回,返回的是协程对象。传入的参数是future或协程构成的可迭代对象。最后将返回值传给run_until_complete()加入事件循环

3.2 携程函数

协程函数,定义函数的时候使用async def 函数名
协程对象,执行写成函数()得到的学成对象

# d定义协程函数
async def func():
    pass
# 获取写成对象
result = func()
print(result) # <coroutine object func at 0x000002326168AD40>

注意:执行协程函数创建协程对象,函数内部代码不会执行。

如果想要运行协程函数内部代码,必须要讲协程对象交给事件循环来处理。

import asyncio 

async def func():
    print("执行协程函数!")

result = func()

loop = asyncio.get_event_loop()
loop.run_until_complete(result)

python3.7有个更简便的方法

import asyncio 

async def func():
    print("执行协程函数!")

result = func()

# loop = asyncio.get_event_loop()
# loop.run_until_complete( result )
asyncio.run(result) # python3.7 

3.3 await

await + 可等待的对象(协程对象、Future、Task对象 -> IO等待)
示例一

import asyncio

async def func():
    print("开始")
    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操作完成之后再继续往下执行。当前协程挂起时,事件循环可以去执行其他协程(任务)。
    response = await others()

    print("IO请求结束,结果为:", response)
async def func1():
    print('func1')
tasks = [
    func(),
    func1()
]
asyncio.run(asyncio.wait(tasks))

3.4 Task对象

Tasks用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。除了使用 asyncio.create_task() 函数以外,还可以用低层级的 loop.create_task()ensure_future() 函数。不建议手动实例化 Task 对象。

注意:asyncio.create_task() 函数在 Python 3.7 中被加入。在 Python 3.7 之前,可以改用低层级的 asyncio.ensure_future() 函数。
示例一:

import asyncio


async def func():
    print(1)
    await asyncio.sleep(2)
    print(2)
    return "返回值"


async def main():
    print("main开始")
	
 	# 创建Task对象,将当前执行func函数任务添加到事件循环。
    task1 = asyncio.create_task( func() )
	
    # 创建Task对象,将当前执行func函数任务添加到事件循环。
    task2 = asyncio.create_task( func() )

    print("main结束")

    # 当执行某协程遇到IO操作时,会自动化切换执行其他任务。
    # 此处的await是等待相对应的协程全都执行完毕并获取结果
    ret1 = await task1
    ret2 = await task2
    print(ret1, ret2)


asyncio.run( main() )

示例二:

import asyncio


async def func():
    print(1)
    await asyncio.sleep(2)
    print(2)
    return "返回值"


async def main():
    print("main开始")
	
	# name给任务起个名字
    task_list = [
        asyncio.create_task(func(), name='n1'),
        asyncio.create_task(func(), name='n2')
    ]

    print("main结束")
	# done是任务列表返回的集合,pending未完成任务的集合
    done, pending = await asyncio.wait(task_list, timeout=None)
    
    print(done)
	# {<Task finished name='n1' coro=<func() done, defined at C:/Users/86166/Deskto/a.py:4> result='返回值'>, <Task finished name='n2' coro=<func() done, defined at C:/Users/86166/Desktop/a.py:4> result='返回值'>}

asyncio.run(main())

示例三:

import asyncio


async def func():
    print(1)
    await asyncio.sleep(2)
    print(2)
    return "返回值"


task_list = [
    func(),
	func(), 
]

done,pending = asyncio.run( asyncio.wait(task_list) )
print(done)
上一篇:Python asyncio异步学习笔记


下一篇:python异步编程之asyncio(百万并发)-晒酷学院