Где присоединиться к потокам, созданным в обработчике запросов асинхронного торнадо?

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

Наверное, лучше всего начать с некоторого упрощенного кода:

class Handler(tornado.web.RequestHandler):

  def perform(self):
     #do something cuz hey, we're in a thread!

  def initialize(self):
    self.thread = None

  @tornado.web.asynchronous
  def post(self):

    self.thread = threading.Thread(target=self.perform)
    self.thread.start()

    self.write('In the request')
    self.finish()

  def on_connection_close(self):
    logging.info('In on_connection_close()')
    if self.thread:
      logging.info('Joining thread: %s' % (self.thread.name))
      self.thread.join()

Моя проблема в том, что on_connection_close никогда не набирается, запросы обрабатываются просто отлично. Во-вторых, я делаю что-то ужасное, вводя таким образом поток?

Ответ 1

Я считаю, что Thread.join() будет блокироваться до тех пор, пока поток не закончится, возможно, чего-то, чего вы хотите избежать. Вместо того, чтобы присоединяться, вы можете иметь обратный вызов потока для обработчика.

При использовании потоков помните, что торнадо не является потокобезопасным, поэтому вы не можете использовать какие-либо методы RequestHandler (например) из потоков.

Это работает для меня:

import functools
import time
import threading
import logging

import tornado.web
import tornado.websocket
import tornado.locale
import tornado.ioloop

class Handler(tornado.web.RequestHandler):
    def perform(self, callback):
        #do something cuz hey, we're in a thread!
        time.sleep(5)
        output = 'foo'
        tornado.ioloop.IOLoop.instance().add_callback(functools.partial(callback, output))

    def initialize(self):
        self.thread = None

    @tornado.web.asynchronous
    def get(self):
        self.thread = threading.Thread(target=self.perform, args=(self.on_callback,))
        self.thread.start()

        self.write('In the request')
        self.flush()

    def on_callback(self, output):
        logging.info('In on_callback()')
        self.write("Thread output: %s" % output)
        self.finish()

application = tornado.web.Application([
    (r"/", Handler),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

Вы можете проверить его с помощью curl --no-buffer localhost:8888. Некоторые браузеры (Safari), похоже, ждут закрытия соединения, прежде чем отображать какой-либо вывод, который немного отбросил меня.

Ответ 2

AFAIK, on_connection_close вызывается только тогда, когда клиент завершает соединение, что может объяснить вашу проблему. Что касается потоковой передачи, я не знаю, что вы хотите сделать, но я не понимаю, почему вы хотите создать поток в запросе Tornado, поскольку одним из преимуществ Tornado является то, что вам не нужно использовать потоки, Если бы я добавил join к вашему примеру, я бы поставил его перед self.finish(), однако вы, вероятно, просто можете опустить его... это будет зависеть от того, что вы хотите делать с потоком, но помните, что Tornado является однопоточным, и весь процесс будет блокироваться, если поток не будет завершен к моменту join().