Tornado and multithreading

1,701 views
Skip to first unread message

Aaron Lewis

unread,
Sep 7, 2016, 10:21:54 PM9/7/16
to Tornado Web Server
I'm using tornado with threads.

In short, each time the websocket handler receives a requests, it start to execute a task, which might take a few minutes.

However, once a client is connected, no other client can be connected, until the first one disconnects. 

Any ideas?



I've attached a minimal example that uses `time.sleep` to simulate long running tasks.


 import tornado.web
    import tornado.websocket
    import tornado.httpserver
    import tornado.ioloop
    import time
    import json
    import threading
    
    class TaskHandler(tornado.websocket.WebSocketHandler):
        def open(self):
            pass
    
        def check_origin(self, origin):
            return True
    
        def on_message(self, message):
            try:
                print 'received: ', message
                self.write_message(json.dumps({'status': 'running'}))
    
                def worker_A(kwargs):
                    time.sleep(100)
                    pass
    
                def worker_B(kwargs):
                    time.sleep(100)
                    pass
    
                threads = []
                for target in [worker_A, worker_B]:
                    t = threading.Thread(target = target, args = ({'xxx': 'yyy'}, ))
                    t.daemon = True
                    t.start()
                    threads.append(t)
    
                for t in threads:
                    t.join()
    
            except Exception, e:
                print 'TaskHandler: exception: ', e
                pass
    
            self.write_message(json.dumps({'status': 'done'}))
     
        def on_close(self):
            pass
    
    class Server(tornado.web.Application):
        def __init__(self):
            handlers = [
                ('/task', TaskHandler),
            ]
    
            tornado.web.Application.__init__(self, handlers)
     
    if __name__ == '__main__':
        server  = tornado.httpserver.HTTPServer(Server())
        server.listen(8765, address = '127.0.0.1')
        tornado.ioloop.IOLoop.instance().start()


Phyo Arkar

unread,
Sep 8, 2016, 1:25:47 AM9/8/16
to Tornado Web Server
Tornado is designed to avoid thread.
You are doing it wrong.

--
You received this message because you are subscribed to the Google Groups "Tornado Web Server" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python-tornad...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Christopher Lege

unread,
Sep 8, 2016, 5:29:00 AM9/8/16
to Tornado Web Server
Hi,

there is one way to do that:

When you receive the message you put the message in a tornado-queue (http://www.tornadoweb.org/en/stable/queues.html) like:
self.command_queue.put_nowait(new_user)
Where new_user contains the websocket to send messages from somewhere else.

Then you have a handler which reads messages from the queue (that is because websockets can not do async for now afaik).
That handler has methods which can execute your longrunning task in threads with the decorator: tornado.concurrent.run_on_executor (http://www.tornadoweb.org/en/stable/concurrent.html)
This can be yielded and therefore new clients can connect and can be asynchronously handled. Make sure that you have enough threads to handle concurrent users. If all threads are used you can put your messages into the queue again or defer them with timeout call and so on.

best

Michael DePalatis

unread,
Sep 8, 2016, 6:30:49 AM9/8/16
to Tornado Web Server
Hi,


On Thursday, September 8, 2016 at 4:21:54 AM UTC+2, Aaron Lewis wrote:
I'm using tornado with threads.

In short, each time the websocket handler receives a requests, it start to execute a task, which might take a few minutes.

However, once a client is connected, no other client can be connected, until the first one disconnects.

 
This appears to block because you are joining the threads. `join` specifically blocks until the thread terminates. So while you're sleeping for 100 seconds, you've blocked the IO loop and nothing else will happen. Tornado (and IO loops in general) are usually intended to be single-threaded. In practice, it works fine to use additional threads for some background work, so long as it isn't CPU-intensive (since Python has the GIL). A better approach than using threads directly would be to run tasks in an executor with the `run_on_executor` decorator.

Michael DePalatis

unread,
Sep 8, 2016, 6:34:01 AM9/8/16
to Tornado Web Server
Hi,

On Thursday, September 8, 2016 at 11:29:00 AM UTC+2, Christopher Lege wrote:
Hi,

that is because websockets can not do async for now afaik


Strictly speaking websockets are already async. You just can't make `on_message` a coroutine. It's easy enough to spawn a long-running coroutine from `on_message` and handle responses from there. See Ben's answer here.

Mike

Christopher Lege

unread,
Sep 8, 2016, 9:52:58 AM9/8/16
to Tornado Web Server
as Ben also stated: the usage of queues and locks to avoid handling concurrency issues with the websockets (because they are async)
Reply all
Reply to author
Forward
0 new messages