python协程基础知识(三)Asyncio 控制子任务Task与Future

前面已经提到怎么使用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() 设置结果。

你可能感兴趣的:(Python,并行编程)