发现什么阻止了事件循环

时间:2019-07-24 19:36:10

标签: python debugging profiling asyncio

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

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

这使程序无法正常工作,因为某些任务需要在5秒内回答其网络连接上的消息。

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

我认为相关代码可能在此处,第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.

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

过去5个不眠之夜,我试图修复自己的代码,但还没有成功。

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

我尝试过的所有概要分析/调试工具都给我不了什么应该解决的线索。而且以不同的方式重写相同的程序约15次之后,我仍然无法正常运行。

我只是一个非专业的程序员,还是Python的新手,但是如果我不能解决这个问题,那么下一步将是学习学习Rust,这本身将是一个巨大的痛苦,大概在三年后我开始工作,将使该功能正常运行,该过程应该不会超过2个月。

2 个答案:

答案 0 :(得分:1)

顺便说一句,asyncio 内部有一个很酷的功能(你可以看代码来源:here),它会告诉你是否有“阻塞”功能。

您只需要启用调试模式(适用于负载测试)。

如何启用调试模式 - 您可以找到here所有选项如何。

答案 1 :(得分:0)

只需编辑文件/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)

第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.

现在,每次异步代码阻塞事件循环3秒钟,它将显示一条消息。

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