异步功能以同步(串行)方式运行,任务相互阻塞

时间:2018-02-21 05:21:22

标签: python python-asyncio

我没有使用asyncio加速。此代码段仍然以与同步作业相同的方式运行。大多数示例使用asyncio.sleep()来强加延迟,我的问题是,如果部分代码根据输入参数产生延迟,那该怎么办呢。

async def c(n):
    #this loop is supposed to impose delay
    for i in range(1, n * 40000):
        c *= i
   return n

async def f():
    tasks = [c(i) for i in [2,1,3]]
    r=[]
    completed, pending = await asyncio.wait(tasks)
    for item in completed:
        r.append(item.result())

    return r

if __name__=="__main__":
    loop = asyncio.get_event_loop()
    k=loop.run_until_complete(f())
    loop.close()

我希望得到[1,2,3],但我没有(连续跑也没有时间差异)

1 个答案:

答案 0 :(得分:4)

asyncio不是为了获得加速,而是在异步环境中编程时避免“回调地狱”,例如(但不限于)非阻塞IO。由于问题中的代码不是异步的,因此使用asyncio无法获得任何好处 - 但您可以考虑使用multiprocessing

在上面的例子中,该函数被定义为async,但它运行整个计算而不等待任何事情。它还包含对未分配变量的引用,所以让我们从运行的版本开始:

async def long_calc(n):
    p = 1
    for i in range(1, n * 10000):
        p *= i
    print(math.log(p))
    return p

最后的print会立即显示计算完成的时间。使用asyncio.gather

完成“并行”启动多个此类协同程序
async def wait_calcs():
    return await asyncio.gather(*[long_calc(i) for i in [2, 1, 3]])

asyncio.gather将让计算运行并在所有计算完成后返回,并按照它们在参数列表中出现的顺序返回结果列表。但运行loop.run_until_complete(wait_calcs())时打印的输出显示计算并非真正并行运行:

178065.71824964616
82099.71749644238
279264.3442843094

结果对应[2, 1, 3]订单。如果协同程序并行运行,那么最小的数字将首先出现,因为它的协程到目前为止工作量最少。

我们可以通过在内循环中引入无操作睡眠来强制协程给其他协同程序运行的机会:

async def long_calc(n):
    p = 1
    for i in range(1, n * 10000):
        p *= i
        await asyncio.sleep(0)
    print(math.log(p))
    return p

输出现在显示协同程序并行运行:

82099.71749644238
178065.71824964616
279264.3442843094

请注意,此版本还需要更多时间才能运行,因为它涉及协同程序和主循环之间的更多切换。只需在一百个左右的时间内睡一次即可避免减速。