影分身术之-asyncio_part1

创建一个协程

1
2
3
4
5
6
7
8
import time
import asyncio

now = lambda: time.time()

# 定义协程函数
async def do_some_work(x):
print('Waiting: ', x)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
start = now()

"""
获取协程对象,协程对象的调用不会立刻执行函数
协程对象需要注册到事件循环上,由事件循环调用
"""
coroutine = do_some_work(2)

# 获取事件循环对象
loop = asyncio.get_event_loop()

# 调用协程对象
loop.run_until_complete(coroutine)

print('TIME: ', now() - start)
Waiting:  2
TIME:  0.0010256767272949219

创建一个 task

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
start = now()

coroutine = do_some_work(3)

loop = asyncio.get_event_loop()

"""
ensure_future() 返回的也是 Furure 对象,
内部同样是调用的 create_task()
task = asyncio.ensure_future(coroutine)
"""
task = loop.create_task(coroutine)

print(task)
loop.run_until_complete(task)
print(task)

print('TIME: ', now() - start)
<Task pending coro=<do_some_work() running at <ipython-input-12-3ef64d36efb6>:7>>
Waiting:  3
<Task finished coro=<do_some_work() done, defined at <ipython-input-12-3ef64d36efb6>:7> result=None>
TIME:  0.0009589195251464844

所谓 task 对象本质上是一个 Future 对象(是 Future 的子类)。Future 类保存了协程运行后的状态,用于未来获取协程的结果。

task 在加入事件循环之前是 pending 状态。执行完成之后为 finished 状态。

asyncio.ensure_future(coroutine)loop.create_task(coroutine) 都可以创建一个 task,asyncio.ensure_future(coroutine) 内部就是通过调用 loop.create_task(coroutine) 返回的 task 对象。

绑定回调

我们修改一下 do_some_work() 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async def do_some_work(x):
print('Waiting: ', x)
return 'Done after {}s'.format(x)

def done_callback(future):
print('Callback: ', future.result())

start = now()

coroutine = do_some_work(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)

# 给 task 对象添加回调函数
task.add_done_callback(done_callback)
loop.run_until_complete(task)

print('TIME: ', now() - start)
Waiting:  2
Callback:  Done after 2s
TIME:  0.0015606880187988281

Future 对象的 add_done_callback() 方法接受一个回调函数对象,并且会把自己也传过去。等 task 调用结束时,就可以通过 task 对象的 result() 方法获取调用返回值。

如果有这么一种需求,当回调的时候,我们还需要往回调函数传入多个参数。这种情况可以通过偏函数解决:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import functools

# 注意参数传入的顺序
def done_callback_part(op, future):
print('Callback: ', op, future.result())

start = now()

coroutine = do_some_work(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)

# 给 task 对象添加回调函数
# 注意参数传入的顺序
task.add_done_callback(functools.partial(done_callback_part, "other param"))
loop.run_until_complete(task)

print('TIME: ', now() - start)
Waiting:  2
Callback:  other param Done after 2s
TIME:  0.00046443939208984375

阻塞和 await

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async def do_some_work(x):
print('Waiting: ', x)
await asyncio.sleep(x)
return 'Done after {}s'.format(x)

start = now()

coroutine = do_some_work(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
loop.run_until_complete(task)

print("Task result: ", task.result())
print('TIME: ', now() - start)
Waiting:  2
Task result:  Done after 2s
TIME:  2.0026657581329346

当 sleep 的时候,使用 await 让出控制权。即当程序执行到耗时操作时,使用 await 方法将协程的控制权让出,以便 loop 调用其他协程。

上面的例子都只有一个协程,所以看不出有什么效果。接下来我们就上多个协程瞧瞧。