Отслеживание висящих тем в питоне - PullRequest
0 голосов
/ 21 марта 2019

У меня есть приложение на основе 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 ]

2 голосов
/ 21 марта 2019

Есть идеи, почему, как и что это за ThreadPoolExecutor?

ThreadPoolExecutor - реализация пула потоков, предоставляемая concurrent.futures модуль.Он используется для асинхронного выполнения синхронного кода путем передачи его в отдельный поток.Цель пула - избежать задержек создания и присоединения потока для каждой отдельной задачи;вместо этого пул создает рабочий поток только один раз и сохраняет его в пуле для последующего использования.Максимальное количество потоков в пуле может быть настроено, и по умолчанию количество ядер умножено на 5.

Потоки, которые вы видите в своем коде, принадлежат ThreadPoolExecutor, созданному одной из используемых вами библиотек.,В частности, asyncio создает исполнителя для использования методом run_in_executor.Этот исполнитель используется самой asyncio для предоставления асинхронного интерфейса для вызовов, которые изначально не имеют такой, например, для разрешения DNS, предоставляемого ОС.

В общем случае при использовании нетривиальных сторонних библиотек нельзя предполагать,что ваш код будет единственным для создания потоков.Выполняя итерации по живым потокам, вы просто игнорируете те, которые не создавали, чего можно достичь, например, пометив созданные потоки с помощью пользовательского атрибута объекта Thread.

0 голосов
/ 21 марта 2019

Возможно, есть какой-то способ увидеть, где в коде он создан или какой пакет его создает?

Да, как уже упоминалось в предыдущем ответе, это был исполнитель по умолчанию для 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())

Это приводило к хорошему отслеживанию каждый разновое задание отправлено.

...