有没有办法利用 asyncio 的多个 CPU 内核?

Posted

技术标签:

【中文标题】有没有办法利用 asyncio 的多个 CPU 内核?【英文标题】:Is there a way to take advantage of multiple CPU cores with asyncio? 【发布时间】:2021-05-10 14:46:51 【问题描述】:

我用 python 和 asyncio 创建了一个简单的 HTTP 服务器。但是,我读到基于异步的服务器只能利用一个 CPU 内核。我正在尝试使用multiprocessing 解决此问题,但它不起作用。当我尝试创建一个进程时,它给了我一个错误,说它不能从 _winapi 创建进程。到目前为止,这是我的代码:

def serve_forever(self, host, port):
    srv, loop = self.init(host, port)
    print()
    if self.name:
        print('* Serving App '.format(self.name))
    print('* Serving On http://host:port'.format(host=host, port=port))
    print('* Press <CTRL-C> To Quit')
    workers = []
    try:
        for i in range(mp.cpu_count()-1):
            p = mp.Process(target=loop.run_forever)
            workers.append(p)
            p.start()
        loop.run_forever()
    except KeyboardInterrupt:
        print()
        for p in workers:
            p.terminate()
        srv.close()
        loop.run_until_complete(srv.wait_closed())
        loop.close()
        pass

顺便说一句,self.init 函数有效。

【问题讨论】:

【参考方案1】:

我认为,您可能有点混淆了并行编程和并发编程。乍一看,它们可能看起来很相似,但您很快就会发现它们非常不同。

Asyncio 有助于并发,这意味着您可以以非阻塞方式编写代码。换句话说,对于需要时间来响应的 I/O 操作,例如网络调用或磁盘访问,您可以在等待响应时让一段特定的代码不阻塞您的进程。这为 同一线程中代码的其他异步部分释放 CPU 周期。

并行编程涉及将一些更高级别任务的一小部分委派给多个进程或线程,并且(通常)在所有完成后收集和合并结果。

以下三种情况有助于区分:

您可以编写一个服务器程序,以便接收到的每个请求都由一个新线程处理。该线程可能是 100% 阻塞的,因此如果它进行网络调用或从磁盘读取文件,它将等待 I/O 任务完成。但这没关系,因为它在自己的线程中,操作系统将负责切换哪些线程何时运行、在哪些内核上运行等,因此其他线程将有机会在该线程等待 I/O 时运行.这样做的缺点是线程存在资源开销,并且操作系统对线程内发生的事情没有完全了解,它只是尽其所能确保它们都得到公平的转折。

另一个版本的服务器可以以并发方式编写。这里只使用了一个线程,但是该线程对阻塞和执行的内容有详细的了解(asyncio to the rescue),所以你可以编写一次只处理一个请求的代码,但是当给定的请求正在等待时数据它允许另一个请求进行一些处理,在任务之间切换而其他任务被阻塞,所有这些都在同一个线程/进程中。这是一种更有效的资源使用方式,但它通常只适合高 I/O 工作负载,例如读取/写入数据库的简单服务器。对于必须为每个请求执行大量大型计算的服务器来说,这不是很好,因为在计算过程中不会有 I/O 事件来触发任务切换。

第三种情况是将这两个概念结合起来。这有助于扩展处理大量 I/O 操作的异步服务器。它也可以用于需要处理大量连接和长时间运行的任务,其中任务被委派给线程或其他更复杂的配置,但实际上它对于扩展最有用。

Asyncio 有一些 built in support for subprocesses。

我强烈推荐阅读this article,非常好。

【讨论】:

我说的是可以使用所有 cpu 核心的异步 HTTP 服务器。 我不建议您尝试在这里推出自己的产品。有标准工具可以做到这一点。见Gunicorn 和这个example stack 恐怕我做不到。 Gunicorn 适用于 unix 系统,但不适用于 windows。

以上是关于有没有办法利用 asyncio 的多个 CPU 内核?的主要内容,如果未能解决你的问题,请参考以下文章

Windows 上程序的 CPU 使用率测量

Goroutines vs asyncio 任务 + CPU-bound 调用的线程池

13.并发编程之协程

各位高手有没有办法把GPU虚拟为CPU使用?

如何在主异步循环内的同步子进程内运行多个异步循环?

多线程如何利用多个内核?