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?
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.
Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With