前面已经提到怎么使用Asyncio来控制协程任务的执行,它还提供了 asyncio.Task() 类,可以在任务中使用协程。它的作用是,在同一事件循环中,多组任务被包含执行。
"""
Asyncio using Asyncio.Task to execute three math function in parallel
"""
import asyncio
@asyncio.coroutine
def factorial(number):
f = 1
for i in range(2, number + 1):
print("Asyncio.Task: Compute factorial(%s)" % (i))
yield from asyncio.sleep(1)
f *= i
print("Asyncio.Task - factorial(%s) = %s" % (number, f))
@asyncio.coroutine
def fibonacci(number):
a, b = 0, 1
for i in range(number):
print("Asyncio.Task: Compute fibonacci (%s)" % (i))
yield from asyncio.sleep(1)
a, b = b, a + b
print("Asyncio.Task - fibonacci(%s) = %s" % (number, a))
@asyncio.coroutine
def binomialCoeff(n, k):
result = 1
for i in range(1, k+1):
result = result * (n-i+1) / i
print("Asyncio.Task: Compute binomialCoeff (%s)" % (i))
yield from asyncio.sleep(1)
print("Asyncio.Task - binomialCoeff(%s , %s) = %s" % (n, k, result))
if __name__ == "__main__":
tasks = [asyncio.Task(factorial(10)),
asyncio.Task(fibonacci(10)),
asyncio.Task(binomialCoeff(20, 10))]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
运行结果:
Asyncio.Task: Compute factorial(2)
Asyncio.Task: Compute fibonacci (0)
Asyncio.Task: Compute binomialCoeff (1)
Asyncio.Task: Compute factorial(3)
Asyncio.Task: Compute fibonacci (1)
Asyncio.Task: Compute binomialCoeff (2)
Asyncio.Task: Compute factorial(4)
Asyncio.Task: Compute fibonacci (2)
Asyncio.Task: Compute binomialCoeff (3)
Asyncio.Task: Compute factorial(5)
Asyncio.Task: Compute fibonacci (3)
Asyncio.Task: Compute binomialCoeff (4)
Asyncio.Task: Compute factorial(6)
Asyncio.Task: Compute fibonacci (4)
Asyncio.Task: Compute binomialCoeff (5)
Asyncio.Task: Compute factorial(7)
Asyncio.Task: Compute fibonacci (5)
Asyncio.Task: Compute binomialCoeff (6)
Asyncio.Task: Compute factorial(8)
Asyncio.Task: Compute fibonacci (6)
Asyncio.Task: Compute binomialCoeff (7)
Asyncio.Task: Compute factorial(9)
Asyncio.Task: Compute fibonacci (7)
Asyncio.Task: Compute binomialCoeff (8)
Asyncio.Task: Compute factorial(10)
Asyncio.Task: Compute fibonacci (8)
Asyncio.Task: Compute binomialCoeff (9)
Asyncio.Task - factorial(10) = 3628800
Asyncio.Task: Compute fibonacci (9)
Asyncio.Task: Compute binomialCoeff (10)
Asyncio.Task - fibonacci(10) = 55
Asyncio.Task - binomialCoeff(20 , 10) = 184756.0
由上面结果看出,factorial,fibonacci,binomialCoeff三个子任务是被包含在Task当中执行,当yield from asyncio.sleep的时候,任务会被挂起,事件循环在其他的子任务继续运行,无须用户制定下一个将要运行的子任务,直到各个子任务完成之后返回。
factorial, fibonacci 和 binomialCoeff ,每一个都带有asyncio.coroutine 装饰器:
@asyncio.coroutine
def factorial(number):
do Something
@asyncio.coroutine
def fibonacci(number):
do Something
@asyncio.coroutine
def binomialCoeff(n, k):
do Something
为了能并行执行这三个任务,我们将其放到一个task的list中:
if __name__ == "__main__":
tasks = [asyncio.Task(factorial(10)),
asyncio.Task(fibonacci(10)),
asyncio.Task(binomialCoeff(20, 10))]
得到事件循环:
loop = asyncio.get_event_loop()
然后运行任务
loop.run_until_complete(asyncio.wait(tasks))
这里, asyncio.wait(tasks) 表示运行直到所有给定的协程都完成。
最后,关闭事件循环:
loop.close()
协程知识拓展 Future
Asyncio 模块的另一个重要的组件是 Future 类,asyncio.Futures 类代表还未完成的结果(有可能是一
个Exception)
要操作Asyncio中的 Future ,必须进行以下声明:
import asyncio
future = asyncio.Future()
基本的方法有:
• cancel(): 取消future的执行,调度回调函数
• result(): 返回future代表的结果
• exception(): 返回future中的Exception
• add_done_callback(fn): 添加一个回调函数,当future执行的时候会调用这个回调函数
• remove_done_callback(fn): 从“call whten done”列表中移除所有callback的实例
• set_result(result): 将future标为执行完成,并且设置result的值
• set_exception(exception): 将future标为执行完成,并设置Exception
# -*- coding: utf-8 -*-
"""
Asyncio.Futures - Chapter 4 Asynchronous Programming
"""
import asyncio
import sys
@asyncio.coroutine
def first_coroutine(future, N):
"""前n个数的和"""
count = 0
for i in range(1, N + 1):
count = count + i
yield from asyncio.sleep(4)
future.set_result("first coroutine (sum of N integers) result = " + str(count))
@asyncio.coroutine
def second_coroutine(future, N):
count = 1
for i in range(2, N + 1):
count *= i
yield from asyncio.sleep(3)
future.set_result("second coroutine (factorial) result = " + str(count))
def got_result(future):
print(future.result())
if __name__ == "__main__":
N1 = int(sys.argv[1])
N2 = int(sys.argv[2])
loop = asyncio.get_event_loop()
future1 = asyncio.Future()
future2 = asyncio.Future()
tasks = [
first_coroutine(future1, N1),
second_coroutine(future2, N2)]
future1.add_done_callback(got_result)
future2.add_done_callback(got_result)
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
$ python asy.py 1 1
first coroutine (sum of N integers) result = 1
second coroutine (factorial) result = 1
$ python asy.py 2 2
first coroutine (sum of N integers) result = 3
second coroutine (factorial) result = 2
$ python asy.py 3 3
first coroutine (sum of N integers) result = 6
second coroutine (factorial) result = 6
$ python asy.py 4 4
first coroutine (sum of N integers) result = 10
second coroutine (factorial) result = 24
在主程序中,我们通过定义future对象和协程联系在一起:
if __name__ == "__main__":
...
future1 = asyncio.Future()
future2 = asyncio.Future()
定义tasks的时候,将future对象作为变量传入协程中:
tasks = [
first_coroutine(future1, N1),
second_coroutine(future2, N2)]
最后,添加一个 future 执行时的回调函数:
def got_result(future):
print(future.result())
4.6. 使用Asyncio和Futures
在我们传入future的协程中,在计算之后我们分别添加了3s、4s的睡眠时间:
yield from asyncio.sleep(4)
然后,我们将future标为完成,通过 future.set_result() 设置结果。