0

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

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

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

在此处输入图像描述

任何想法为什么,如何以及这个 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
4

2 回答 2

2

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

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

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

通常,当使用非平凡的第三方库时,您不能假设您的代码将是唯一创建线程的代码。在迭代活动线程时,您只需忽略那些您没有创建的线程,例如,可以通过在Thread对象上使用自定义属性标记您创建的线程来完成。

于 2019-03-21T09:26:40.483 回答
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())

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

于 2019-03-21T13:44:24.493 回答