simonmysun
simonmysun

Reputation: 488

How to avoid too many threads when using tornado.ioloop.IOLoop.run_in_executor?

I'm using tornado.ioloop.IOLoop.run_in_executor to change a synchronous function to asynchrounous, but it turns out that each time the function is called, there is a thread created but not killed.

Here's an mimimal reproducible example (at least reproducible on my machine):

#!/usr/bin/env python3                                                          

import time
import tornado.ioloop
import tornado.web

def slow_func():
    time.sleep(1)
    print('slow func returned')
    return 'succ\n'

class TestHandler(tornado.web.RequestHandler):
    async def get(self):
        print('GET called')
        try:
            result = await tornado.ioloop.IOLoop.current().run_in_executor(None, slow_func)
        except Exception as e:
            print(e)
        self.write(result)
        print('GET returned')

if __name__ == '__main__':
    tornado.web.Application([
        (r'/', TestHandler),
    ]).listen(3000)
    print('Serving at 3000')
    tornado.ioloop.IOLoop.current().start()

Each request to this TestHandler will create a new thread to run the slow_func, but the thread remains after the function returns. I can see them in ps H and it creates new threads until it hits ulimit. My environment here is:

$ uname -a
Linux xxx 2.6.32-754.6.3.el6.x86_64 #1 SMP Tue Sep 18 10:29:08 EDT 2018 x86_64 x86_64 x86_64 GNU/Linux
$ python3 --version
Python 3.7.4
$ pip3 show tornado
Name: tornado
Version: 6.0.3
Summary: Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed.
Home-page: http://www.tornadoweb.org/
Author: Facebook
Author-email: [email protected]
License: http://www.apache.org/licenses/LICENSE-2.0
Location: /xxx/lib/python3.7/site-packages
Requires: 
Required-by:

tornado.ioloop.IOLoop.run_in_executor uses concurrent.futures.Executor and returns an awaitable Future object. [1][2][3]

What are the threads doing after the functions return? Why aren't they killed after the future object resolves? How should I avoid this?

Upvotes: 2

Views: 2573

Answers (1)

xyres
xyres

Reputation: 21779

run_in_executor takes a concurrent.futures.Executor object as the first argument.

You can create an executor and limit the size of the thread pool:

from concurrent.futures import ThreadPoolExecutor

executor = ThreadPoolExecutor(max_workers=8)

IOLoop.current().run_in_executor(executor, slow_func) 

Upvotes: 3

Related Questions