Tornado multiple IOLoop in multithreads

2019-07-13 02:29发布

问题:

I am trying to run multiple IOLoop in multiple threads and I am wondering how the IOLoop actually works.

class WebThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self, name='WebThread')

    def run(self):
        curdir = os.path.dirname(os.path.realpath(__file__))

        application = Application() #Very simple tornado.web.Application
        http_server_api = tornado.httpserver.HTTPServer(application)
        http_server_api.listen(8888)

        logging.info('Starting application')

        #tornado.ioloop.IOLoop.instance() is singleton, not for thread, right?

        ioloop = tornado.ioloop.IOLoop()
        ioloop.make_current()
        ioloop.start()

According to the doc, I can not use IOLoop.instance() since it's a singleton and I am working in a thread. So I created my own IOLoop. But this piece of code listens on the port 8888 but can not render any web page. I am wondering is there anything missed, or do I need to tie the http_server to the IOLoop in some way?

Also, I find that removing the last 3 lines and substitute with tornado.ioloop.IOLoop.instance().start works perfectly for single thread. But what's the difference between the singleton and self created IOLoop?

I am new to Tornado and any answers are welcomed.

回答1:

In general you should use IOLoop.current as the default when constructing an asynchronous object, and use IOLoop.instance when you mean to communicate to the main thread from a different one.

IOLoop.current without params returns already created ioloop of thread or it calls IOLoop.instance(). And HTTPServer (actually in TCPServer) use IOLoop.current to interact with ioloop, so the only thing you should change is to create ioloop before HTTPServer e.g.

class WebThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self, name='WebThread')

    def run(self):
        curdir = os.path.dirname(os.path.realpath(__file__))

        ioloop = tornado.ioloop.IOLoop()

        application = Application() #Very simple tornado.web.Application
        http_server_api = tornado.httpserver.HTTPServer(application)
        http_server_api.listen(8888)

        logging.info('Starting application')

        ioloop.start()

Also I've removed IOLoop.make_current, since it's redundant - IOLoop() sets self as a current.


Code above will work, but only with one thread, because reuse_port is not enabled by default. You will end up with:

OSError: [Errno 98] Address already in use

You can enable this with

    http_server_api.bind(port=8888, reuse_port=True)
    http_server_api.start()

instead of http_server_api.listen(8888)