Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where to join threads created in an asynchronous tornado request handler?

This one has me a bit baffled. Fairly new to tornado and threading in python, so I could be completely off the mark with what I'm trying to do here.

Probably best to start with some simplified code:

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()

My problem is that on_connection_close is never getting called, requests are getting handled just fine. Secondly, am I doing something terrible introducing threading in this manner?

like image 231
John Carter Avatar asked May 19 '11 18:05

John Carter


People also ask

Is Tornado multithreaded?

Modern web servers like Flask, Django, and Tornado are all able to handle multiple requests simultaneously. The concept of multitasking is actually very vague due to its various interpretations. You can perform multitasking using multiprocessing, multithreading, or asyncio.

Is Tornado asynchronous?

Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed.

Is Tornado single threaded?

To minimize the cost of concurrent connections, Tornado uses a single-threaded event loop. This means that all application code should aim to be asynchronous and non-blocking because only one operation can be active at a time.

What is Tornado web application?

A Tornado web application generally consists of one or more RequestHandler subclasses, an Application object which routes incoming requests to handlers, and a main() function to start the server.


2 Answers

I believe Thread.join() will block until the thread finishes, probably something you want to avoid. Rather than joining, you can have the thread callback to the handler.

When using threads, be aware that tornado isn't thread-safe, so you can't use any RequestHandler (for example) methods from threads.

This works for me:

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()

You can test it with curl --no-buffer localhost:8888. Some browsers (Safari) seem to wait for the connection to close before displaying any output, which threw me off for a while.

like image 100
Cole Maclean Avatar answered Nov 15 '22 13:11

Cole Maclean


AFAIK, on_connection_close is only called only when the client terminates the connection, which may explain your problem. Regarding threading, I don't know what you want to do, but I can't see why you would want to create a thread in a Tornado request as one of the advantages of Tornado is exactly that you don't have to use threading. If I were to add a join to your example I would put it just before self.finish(), however, you can probably just omit it... that will depend on what you want to do with the thread, but remember that Tornado is single-threaded and the whole process will block if the thread is not finished by the time join() comes.

like image 44
ubik Avatar answered Nov 15 '22 13:11

ubik