Search code examples
pythonmultithreadingpython-3.xtornadopython-asyncio

Is this style of using Thread pool with tornado ok?


So I create a class variable called executor in my class

executor = ThreadPoolExecutor(100)

and instead of having functions and methods and using decorators, I simply use following line to handle my blocking tasks(like io and hash creation and....) in my async methods

result = await to_tornado_future(self.executor.submit(blocking_method, param1, param2)

I decided to use this style cause

1- decorators are slower by nature

2- there is no need for extra methods and functions

3- it workes as expected and creates no threads before it needed

Am I right ? Please use reasons(I want to know if the way I use, is slower or uses more resources or....)

Update

Based on Ben answer, my above approach was not correct so I ended up using following function as needed, I think it's the best way to go

def pool(pool_executor, fn, *args, **kwargs):
    new_future = Future()
    result_future = pool_executor.submit(fn, *args, **kwargs)
    result_future.add_done_callback(lambda f: new_future.set_result(f.result()))
    return new_future

usage:

result = await pool(self.executor, time.sleep, 3)

Solution

  • This is safe as long as all your blocking methods are thread-safe. Since you mentioned doing IO in these threads, I'll point out that doing file IO here is fine but all network IO in Tornado must occur on the IOLoop's thread.

    Why do you say "decorators are slower by nature"? Which decorators are slower than what? Some decorators have no performance overhead at all (although most do have some runtime cost). to_tornado_future(executor.submit()) isn't free either. (BTW, I think you want tornado.gen.convert_yielded instead of tornado.platform.asyncio.to_tornado_future. executor.submit doesn't return an asyncio.Future).

    As a general rule, running blocking_method on a thread pool is going to be slower than just calling it directly. You should do this only when blocking_method is likely to block for long enough that you want the main thread free to do other things in the meantime.