【问题标题】:Tracking dangling threads in python跟踪python中的悬空线程
【发布时间】:2019-03-20 21:13:32
【问题描述】:

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

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

    【讨论】:

      【解决方案2】:

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

      是的,正如前面的回答所提到的,它是 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())
      

      这会在每次提交新任务时产生良好的回溯。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-29
        • 2014-02-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-15
        • 2011-07-11
        相关资源
        最近更新 更多