【问题标题】:Why using Asyncio is not reducing the overall execution time in Python and run functions concurrently?为什么使用 Asyncio 不会减少 Python 中的整体执行时间并同时运行函数?
【发布时间】:2020-09-16 21:36:36
【问题描述】:

我正在尝试使用 asyncio 运行一段代码并减少整个代码的执行时间。下面是我的代码,大约需要 6 秒才能完全执行自己

普通函数调用-(方法一)

from time import time, sleep
import asyncio


def find_div(range_, divide_by):
    lis_ = []
    for i in range(range_):
        if i % divide_by == 0:
            lis_.append(i)
        
    print("found numbers for range {}, divided by {}".format(range_, divide_by))
    return lis_

if __name__ == "__main__":
    start = time()
    find_div(50800000, 341313)
    find_div(10005200, 32110)
    find_div(50000340, 31238)
    print(time()-start)
    

上面代码的输出就是总执行时间6秒

多线程方法-(方法 2) 在这里使用了多线程,但令人惊讶的是时间增加了

from time import time, sleep
import asyncio
import threading


def find_div(range_, divide_by):
    lis_ = []
    for i in range(range_):
        if i % divide_by == 0:
            lis_.append(i)
        
    print("found numbers for range {}, divided by {}".format(range_, divide_by))
    return lis_

if __name__ == "__main__":
    start = time()
    t1 = threading.Thread(target=find_div, args=(50800000, 341313)) 
    t2 = threading.Thread(target=find_div, args=(10005200, 32110)) 
    t3 = threading.Thread(target=find_div, args=(50000340, 31238)) 
  
    t1.start() 
    t2.start() 
    t3.start()

    t1.join() 
    t2.join() 
    t3.join()
    print(time()-start)

以上代码的输出是12 secs

多处理方法-(方法 3)

from time import time, sleep
import asyncio
from multiprocessing import Pool

def multi_run_wrapper(args):
   return find_div(*args)

def find_div(range_, divide_by):
    lis_ = []
    for i in range(range_):
        if i % divide_by == 0:
            lis_.append(i)
        
    print("found numbers for range {}, divided by {}".format(range_, divide_by))
    return lis_

if __name__ == "__main__":
    start = time()
    with Pool(3) as p:
        p.map(multi_run_wrapper,[(50800000, 341313),(10005200, 32110),(50000340, 31238)])
    
    
    print(time()-start)

多处理代码的输出为 3 secs,优于普通函数调用方法。

Asyncio 方法-(方法 4)

from time import time, sleep

import asyncio

async def find_div(range_, divide_by):
    lis_ = []
    for i in range(range_):
        if i % divide_by == 0:
            lis_.append(i)
        
    print("found numbers for range {}, divided by {}".format(range_, divide_by))
    return lis_


async def task():

    tasks = [find_div(50800000, 341313),find_div(10005200, 32110),find_div(50000340, 31238)]
    result = await asyncio.gather(*tasks)
    print(result)

if __name__ == "__main__":
    start = time()
    asyncio.run(task())
    print(time()-start)

上面的代码也需要大约 6 秒,这与 Approach 1 的正常执行函数调用相同。

问题-
为什么我的 Asyncio 方法没有按预期工作并减少了总时间?
代码有什么问题?

【问题讨论】:

  • 我认为这只是示例代码?因为有一种比检查范围内每个数字的模数更有效的方法来获取一个数字的所有倍数的列表
  • 您的“异步”函数不会等待任何内容,因此它们甚至不会尝试切换执行,它们只是作为普通同步代码执行。如果您在循环中添加类似await asyncio.sleep(0) 的内容,它们将开始产生并同时执行。请注意,您不会以这种方式获得加速,甚至可能会证明异步是所有测试变体中最慢的。正如答案中所解释的,这是因为异步执行是为并行等待而设计的,而不是为并行执行而设计的,而且您的代码显然受 CPU 限制。
  • 如果你想在你的程序中使用 asyncio 来处理其他事情(比如网络通信),但是有 CPU 绑定的代码应该在多个内核上执行,你可以使用 @ 结合 multiprocessing 和 asyncio 987654321@.
  • MaxNoe,是的,这只是一个示例/示例代码。 user4815162342,谢谢这个链接看起来很有用,我去看看。

标签: python multithreading async-await multiprocessing python-asyncio


【解决方案1】:

您有专门使用 CPU 的代码。 这样的代码无法使用异步加速。

当你有任务在等待与 CPU 无关的东西时,异步会发光,例如。网络请求或从磁盘读取。这通常适用于所有语言。

在 python 中,基于线程的方法也对您没有帮助,因为这仍然将您限制为单核而不是真正的并行执行。这是由于全局解释器锁 (GIL)。线程之间启动和切换的开销使其比简单版本慢。 在这方面,线程类似于 python 中的异步,只有当你等待的时间不是主要花在 CPU 上或者你正在调用不受 GIL 约束的代码时,它才有帮助,例如c 扩展。

使用multiprocessing确实是使用了多个cpu核心,所以比普通方案要快。

【讨论】:

  • 那么,如果我有一个可能是异步库的数据库连接并执行一些 SQL,那么可以以异步方式添加这些东西,对吧?你能指导我找到一个 pymssql 异步功能的好资源吗?
  • @johnmich 你可以使用 aiomysql 这是一个数据库的异步库。
猜你喜欢
  • 1970-01-01
  • 2012-07-11
  • 2020-09-25
  • 2016-10-24
  • 2020-06-13
  • 2015-05-07
  • 1970-01-01
  • 1970-01-01
  • 2012-12-31
相关资源
最近更新 更多