python - Tornado '@run_on_executor' is blocking -
i ask how tornado.concurrent.run_on_executor (later run_on_executor) works, because not understand how run synchronous task not block main ioloop.
all examples using run_on_executor, found, using time block loop. time module works fine, when try time intesive calculations, using run_on_executor, task blocks ioloop. able see app uses multiple threads, still blocking.
i want use run_on_executor hashing passwords using bcrypt, replaced calculation gain time testing.
here have small app, demonstrate confusion.
from tornado.options import define, options import tornado.web import tornado.httpserver tornado import gen tornado.concurrent import run_on_executor import tornado.httpclient import tornado.escape import time import concurrent.futures import urllib executor = concurrent.futures.threadpoolexecutor(20) define("port", default=8888, help="run on given port", type=int) # should not blocking ? class exphandler(tornado.web.requesthandler): _thread_pool = executor @gen.coroutine def get(self, num): = int(num) result = yield self.exp(2, i) self.write(str(result)) self.finish() @run_on_executor(executor="_thread_pool") def exp(self, x, y): result = x ** y return(result) class nonblockinghandler(tornado.web.requesthandler): @gen.coroutine def get(self): http_client = tornado.httpclient.asynchttpclient() try: response = yield http_client.fetch("http://www.google.com/") self.write(response.body) except tornado.httpclient.httperror e: self.write(("error: " + str(e))) finally: http_client.close() self.finish() class sleephandler(tornado.web.requesthandler): _thread_pool = executor @gen.coroutine def get(self, sec): sec = float(sec) start = time.time() res = yield self.sleep(sec) self.write("sleeped {} s".format((time.time() - start))) self.finish() @run_on_executor(executor="_thread_pool") def sleep(self, sec): time.sleep(sec) return(sec) class application(tornado.web.application): def __init__(self): handlers = [ (r'/exp/(?p<num>[^\/]+)?', exphandler), (r'/nonblocking/?', nonblockinghandler), (r'/sleep/(?p<sec>[^\/]+)?',sleephandler) ] settings = dict( debug=true, logging="debug" ) tornado.web.application.__init__(self, handlers, **settings) def main(): tornado.options.parse_command_line() http_server = tornado.httpserver.httpserver(application()) http_server.listen(options.port) io_loop = tornado.ioloop.ioloop.instance() io_loop.start() if __name__ == "__main__": main() i grateful explanation why exphandler, running in executor blocking loop.
python (at least in cpython implementation) has global interpreter lock prevents multiple threads executing python code @ same time. in particular, runs in single python opcode uninterruptible unless calls c function explicitly releases gil. large exponentation ** holds gil whole time , blocks other python threads, while call bcrypt() release gil other threads can continue work.
Comments
Post a Comment