在python中跟踪悬空线程

问题描述 投票:0回答:2

我有一个基于python 3.7.2 asyncio的应用程序。有一个端点暴露了一些线程信息:

threads_info = {}
for thread in enumerate():
    threads_info[thread.__str__()] = traceback.format_stack(sys._current_frames()[thread.ident])

据我所知,除了主线程之外应该没有运行的线程,但是当我查询端点时,我看到了这个奇怪的ThreadPoolExecutor。它从一个工人开始并不断增加:

enter image description here

任何想法为什么,如何以及什么是这个ThreadPoolExecutor?也许有一些方法可以看到代码在哪里创建或者哪个包创建它?

我用来运行我的应用程序的Dockerfile:

FROM python:3.7.2-alpine as base

FROM base as builder
RUN mkdir /install
WORKDIR /install
COPY requirements /requirements
RUN apk add \
    "gcc>8.2.0" \
    "g++>8.2.0" \
    "libffi-dev>3.2.1" \
    "musl-dev>1.1.20"
RUN pip install --install-option="--prefix=/install" -r /requirements

FROM base
RUN apk add --no-cache procps
COPY --from=builder /install /usr/local
COPY src /app
WORKDIR /app
RUN mkdir logs
ENTRYPOINT ["python", "-u", "app.py"]
EXPOSE 80/tcp

我的要求档案:

quart==0.8.1
aiohttp==3.5.4
cchardet==2.1.4
aiodns==1.2.0
requests==2.21.0
psutil==5.6.1
python-3.x python-asyncio python-multithreading aiohttp quart
2个回答
2
投票

任何想法为什么,如何以及什么是这个ThreadPoolExecutor?

ThreadPoolExecutorconcurrent.futures模块提供的线程池实现。它通过将同步代码交给单独的线程来用于异步执行。该池的目的是避免为每个单独的任务创建和加入线程的延迟;相反,池只创建一次工作线程,并将其保留在池中供以后使用。可以配置池中的最大线程数,默认为核心数乘以5。

您在代码中看到的线程属于由您正在使用的库之一实例化的ThreadPoolExecutor。具体来说,asyncio创建了一个由run_in_executor方法使用的执行程序。 asyncio本身使用此执行程序为本机没有的调用提供异步接口,例如操作系统提供的DNS解析。

通常,在使用非平凡的第三方库时,您不能假设您的代码是唯一创建线程的代码。在实时线程上进行迭代时,您只需忽略那些未创建的线程,这可以通过在Thread对象上使用自定义属性标记您创建的线程来实现。


0
投票

也许有一些方法可以看到代码在哪里创建或者哪个包创建它?

是的,正如之前的回答所提到的,它是asyncio的默认执行者。为了调试哪个包是罪魁祸首,我必须编写自己的执行程序:

class AsyncioDefaultExecutor(ThreadPoolExecutor):

    def __init__(self, thread_name_prefix='', max_workers=None):
        self.logger = get_logger("asyncioTh")
        super(AsyncioDefaultExecutor, self).__init__(thread_name_prefix=thread_name_prefix)

    def submit(self, fn, *args, **kwargs):
        debug_info = "Function " + fn.__name__ + " in " + fn.__code__.co_filename + ":" + \
                     str(fn.__code__.co_firstlineno) + "\n" + "".join(traceback.format_stack())
        self.logger.info(debug_info)
        return super(AsyncioDefaultExecutor, self).submit(fn, *args, **kwargs)

并将其设置为默认执行程序:

loop.set_default_executor(AsyncioDefaultExecutor())

每次提交新任务时都会产生很好的回溯。

© www.soinside.com 2019 - 2024. All rights reserved.