在python中跟踪悬空线程

时间:2019-03-20 21:13:33

标签: python-3.x python-asyncio python-multithreading aiohttp quart

我有一个基于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

2 个答案:

答案 0 :(得分:2)

  

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

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

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

通常,在使用非平凡的第三方库时,您不能假定您的代码将是创建线程的唯一代码。遍历活动线程时,只需忽略未创建的线程即可,例如,可以通过在Thread对象上使用自定义属性标记创建的线程来实现。

答案 1 :(得分: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())

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