Лучшее решение для Python Threading. - PullRequest
7 голосов
/ 14 марта 2012

Я использую довольно стандартный Threading.Event: основной поток попадает в точку, в которой он находится в цикле, который выполняется:

event.wait(60)

Другие блоки в запросе, пока не будет доступен ответ, а затем инициируетa:

event.set()

Я ожидаю, что основной поток будет выбирать в течение 40 секунд, но это не так.Из исходного кода Python 2.7 Lib / threading.py:

# Balancing act:  We can't afford a pure busy loop, so we
# have to sleep; but if we sleep the whole timeout time,
# we'll be unresponsive.  The scheme here sleeps very
# little at first, longer as time goes on, but never longer
# than 20 times per second (or the timeout time remaining).
endtime = _time() + timeout
delay = 0.0005 # 500 us -> initial delay of 1 ms
while True:
   gotit = waiter.acquire(0)
   if gotit:
       break
   remaining = endtime - _time()
   if remaining <= 0:
       break
   delay = min(delay * 2, remaining, .05)
   _sleep(delay)

То, что мы получаем, это системный вызов select, запускаемый каждые 500 мкс.Это вызывает заметную нагрузку на машину с довольно узким циклом выбора.

Может кто-нибудь объяснить, почему задействован баланс и почему он отличается от потока, ожидающего дескриптор файла.

а во-вторых, есть ли лучший способ реализовать спящий основной поток без такого жесткого цикла?

Ответы [ 2 ]

3 голосов
/ 11 марта 2014

Недавно я столкнулся с той же проблемой, и я также отследил ее до этого точного блока кода в модуле threading.

Это отстой.

Решение будетлибо перегрузить модуль потоков, либо перейти на python3, где эта часть реализации была исправлена.

В моем случае переход на python3 был бы огромным усилием, поэтому я выбрал первый.Я сделал следующее:

  1. Я создал быстрый файл .so (используя cython) с интерфейсом для pthread.Он включает в себя функции Python, которые вызывают соответствующие функции pthread_mutex_*, и ссылки на libpthread.В частности, наиболее важной функцией для интересующей нас задачи является pthread_mutex_timedlock .
  2. . Я создал новый модуль threading2 (и заменил все строки import threading в моей кодовой базе наimport threading2).В threading2 я переопределил все соответствующие классы из threading (Lock, Condition, Event), а также из Queue, которые я использую много (Queue и * 1031)*).Класс Lock был полностью повторно реализован с использованием функций pthread_mutex_*, но все остальное было намного проще - я просто разделил на подклассы оригинал (например, threading.Event) и переопределил __init__, чтобы создать мой новый тип Lock.Остальное просто сработало.

Реализация нового типа Lock была очень похожа на оригинальную реализацию в threading, но я основал новую реализацию acquire на найденном мной коде.в модуле python3 threading (что, естественно, намного проще, чем вышеупомянутый блок "балансировки").Эта часть была довольно простой.

(Кстати, результатом в моем случае было ускорение моего многопоточного процесса на 30%. Даже больше, чем я ожидал.)

2 голосов
/ 24 марта 2013

Я полностью с тобой согласен, это отстой.

В настоящее время я придерживаюсь простого вызова select без тайм-аута и слушаю созданный ранее канал. Пробуждение делается путем написания символа в трубе.

См. сон и пробуждение функции от Gunicorn.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...