Tornado ThreadPoolExecutor задерживает запросы при получении результатов - PullRequest
0 голосов
/ 28 января 2019

Спецификация системы - MacOS 10.13.6 - Python 3.7.0 - Tornado 5.1.1

Я хотел бы использовать ThreadPoolExecutor для запуска функций блокировки в экземпляре Tornado, который обслуживаетОТЛИЧНЫЙ сервис.

ThreadPool работает должным образом и порождает четыре рабочих потока параллельно (см. Код и журнал консоли ниже), пока я не пытаюсь выдавать результаты, возвращаемые выполненной функцией.

ThreadPoolExecutor без выдачи результатов

import time
import tornado.web
from tornado.concurrent import run_on_executor
from concurrent.futures import ThreadPoolExecutor
from tornado.ioloop import IOLoop

MAX_WORKERS = 4
i = 0

class Handler(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(max_workers=MAX_WORKERS)

    @run_on_executor
    def background_task(self, i):
        print("going sleep %s" % (i))
        time.sleep(10);
        print("waking up from sleep %s" % (i))
        return str(i)

    @tornado.gen.coroutine
    def get(self):
        global i
        i+=1
        self.background_task(i)


def make_app():
    return tornado.web.Application([
        (r"/", Handler),
    ])


if __name__ == "__main__":
    app = make_app()
    app.listen(8000, '0.0.0.0')
    IOLoop.current().start()        

Вывод на консоль

going sleep 1
going sleep 2
going sleep 3
going sleep 4
waking up from sleep 1
going sleep 5
waking up from sleep 2
going sleep 6
waking up from sleep 3
going sleep 7
waking up from sleep 4
going sleep 8
waking up from sleep 5
going sleep 9
waking up from sleep 6
waking up from sleep 7
waking up from sleep 8
waking up from sleep 9

Как видно, четыре параллельных работника работают, и как только одинзаканчивается, функция в очереди выполняется.

Однако у меня возникают некоторые проблемы, когда я пытаюсь получить возвращаемые функции, используя сопрограмму.Хотя он не полностью блокирует IoLoop, он задерживается без четкой схемы.

Измененный код: ThreadPoolExecutor теперь дает результат

MAX_WORKERS = 4
i = 0

class Handler(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(max_workers=MAX_WORKERS)

    @run_on_executor
    def background_task(self, i):
        print("%s: Going sleep %s" % (time.time(), i))
        time.sleep(10);
        print("%s: Waiking up from sleep %s" % (time.time(), i))
        return str(i)

    @tornado.gen.coroutine
    def get(self):
        global i
        i+=1
        result = yield self.background_task(i)
        self.write(result)

Если посмотреть на вывод консоли, для 1-го и 2-го запроса выполняется только один поток, выполняющий задачи очередипосле завершения одного потока (задержка 10 секунд для запуска задачи 2, а другая - для запуска задачи 3).Однако задачи 3, 4, 5 и 6 выполняются параллельно, но с различными задержками между каждым вызовом.

Вывод на консоль

1548687401.331075: Going sleep 1
1548687411.333173: Waking up from sleep 1
1548687411.340162: Going sleep 2
1548687421.3419871: Waking up from sleep 2
1548687421.347039: Going sleep 3
1548687423.4030259: Going sleep 4
1548687423.884313: Going sleep 5
1548687424.6828501: Going sleep 6
1548687431.351986: Waking up from sleep 3
1548687431.3525162: Going sleep 7
1548687433.407232: Waking up from sleep 4
1548687433.407604: Going sleep 8
1548687433.8846452: Waking up from sleep 5
1548687433.885139: Going sleep 9
1548687434.685195: Waking up from sleep 6
1548687434.685662: Going sleep 10
1548687441.3577092: Waking up from sleep 7
1548687441.358009: Going sleep 11
1548687443.412503: Waking up from sleep 8
1548687443.888705: Waking up from sleep 9
1548687444.691127: Waking up from sleep 10
1548687451.359714: Waking up from sleep 11

Кто-нибудь может объяснить это поведение?У тебя есть какое-нибудь решение?

...