发现是什么阻塞了事件循环

use*_*723 7 python debugging profiling python-asyncio

我有数千个异步任务正在运行。

某些事情大约需要 10 秒才能完成(某些 CPU 密集型工作)。

这使得程序无法工作,因为某些任务需要在其网络连接上回复消息(比如说 5 秒内)。

我当前的想法是以某种方式拦截事件循环。asyncio 模块中必须有某个区域,在每个 epoll()/select() 之间执行事件循环中的所有当前活动任务。如果我可以在每个任务“恢复”之后插入“elapsed = time.time()”和“elapsed = time.time() - elapsed”,我认为这足以找出占用太多时间的任务时间。

我认为相关代码可能在这里,第79行: https: //github.com/python/cpython/blob/master/Lib/asyncio/events.py

def _run(self):
    try:
        self._context.run(self._callback, *self._args)
    except (SystemExit, KeyboardInterrupt):
        raise
    except BaseException as exc:
        cb = format_helpers._format_callback_source(
            self._callback, self._args)
        msg = f'Exception in callback {cb}'
        context = {
            'message': msg,
            'exception': exc,
            'handle': self,
        }
        if self._source_traceback:
            context['source_traceback'] = self._source_traceback
        self._loop.call_exception_handler(context)
    self = None  # Needed to break cycles when an exception occurs.
Run Code Online (Sandbox Code Playgroud)

但我不知道在这里该怎么做才能打印任何有用的信息;我需要一种方法来确定“self._context.run(...)”将执行代码的哪一行。

在过去的 5 个月里,我不眠不休地试图修复我的代码,但还没有成功。

我尝试过使用 CProfiler、line_profile,但它们都没有帮助。它们告诉我执行一个函数所需的时间以及每行花费的时间。我需要找出的是代码在每次循环迭代之间花费了多少时间。

我尝试过的所有分析/调试工具都不告诉我应该修复什么。在以不同方式重写同一个程序大约 15 次之后,我仍然无法让它工作。

我只是一个非专业程序员,而且仍然是 Python 的新手,但如果我不能解决这个问题,下一步将学习 Rust,这本身将是一个巨大的痛苦,可能在我开始 3 年后,我会让这件事工作起来,预计不会超过两个月。

use*_*723 3

刚刚编辑文件 /usr/lib/python3.7/asyncio/events.py 并添加:

import time
import signal
import traceback

START_TIME = 0

def handler(signum, frame):
    print('##########', time.time() - START_TIME)
    traceback.print_stack()

signal.signal(signal.SIGALRM, handler)
Run Code Online (Sandbox Code Playgroud)

第 79 行:

    def _run(self):
        global START_TIME
        try:
            signal.alarm(3)
            START_TIME = time.time()
            self._context.run(self._callback, *self._args)
            signal.alarm(0)
        except Exception as exc:
            cb = format_helpers._format_callback_source(
                self._callback, self._args)
            msg = f'Exception in callback {cb}'
            context = {
                'message': msg,
                'exception': exc,
                'handle': self,
            }
            if self._source_traceback:
                context['source_traceback'] = self._source_traceback
            self._loop.call_exception_handler(context)
        self = None  # Needed to break cycles when an exception occurs.
Run Code Online (Sandbox Code Playgroud)

现在,每当某些异步代码阻塞事件循环 3 秒时,它就会显示一条消息。

发现我的问题是一个简单的“BeautifulSoup(page, 'html.parser')”,其中页面是一个带有大表的 1mb html 文件。