2016-10-16 3 views
0

Я новичок в торнадо и питоне. Пару дней назад я начал писать non-blocking rest api, но еще не смог выполнить миссию. Когда я отправляю два запроса на эту конечную точку «localhost: 8080/async» одновременно, второй запрос получает ответ через 20 секунд! Это объясняет, что я делаю что-то неправильно.Python tornado gen.coroutine блокирует запрос

MAX_WORKERS = 4 
class ASYNCHandler(tornado.web.RequestHandler): 
    executor = ThreadPoolExecutor(max_workers=MAX_WORKERS) 
    counter = 0 

    def pow_task(self, x, y): 
     time.sleep(10) 
     return pow(x,y) 

    async def background_task(self): 
     future = ASYNCHandler.executor.submit(self.pow_task, 2, 3) 
     return future 

    @gen.coroutine 
    def get(self, *args, **kwargs): 
     future = yield from self.background_task() 
     response= dumps({"result":future.result()}, default=json_util.default) 
     print(response) 


application = tornado.web.Application([ 
     ('/async', ASYNCHandler), 
     ('/sync', SYNCHandler), 
    ], db=db, debug=True) 
application.listen(8888) 
tornado.ioloop.IOLoop.current().start() 

ответ

0

Это странно, что возвращение ThreadPoolExecutor будущего, по существу цикл событий блоков смерча. Если кто-либо из команды торнадо прочтет это и знает, почему это так, могут ли они дать объяснение? Я планировал сделать кое-что с потоками в торнадо, но, столкнувшись с этим вопросом, я вижу, что это будет не так просто, как я и предполагал. В любом случае, вот код, который делает то, что вы ожидаете (я обрезается свой оригинальный пример немного вниз, так что любой желающий может запустить его быстро):

from concurrent.futures import ThreadPoolExecutor 
from json import dumps 
import time 
from tornado.platform.asyncio import to_tornado_future 
from tornado.ioloop import IOLoop 
from tornado import gen, web 

MAX_WORKERS = 4 

class ASYNCHandler(web.RequestHandler): 
    executor = ThreadPoolExecutor(max_workers=MAX_WORKERS) 
    counter = 0 

    def pow_task(self, x, y): 
     time.sleep(5) 
     return pow(x,y) 

    async def background_task(self): 
     future = self.executor.submit(self.pow_task, 2, 3) 
     result = await to_tornado_future(future) # convert to tornado future 
     return result 

    @gen.coroutine 
    def get(self, *args, **kwargs): 
     result = yield from self.background_task() 
     response = dumps({"result": result}) 
     self.write(response) 


application = web.Application([ 
     ('/async', ASYNCHandler), 
    ], debug=True) 
application.listen(8888) 
IOLoop.current().start() 

Основные отличия заключаются в методе background_tasks(). Я конвертирую будущее asyncio в будущее tornado, дождитесь результата и верну результат. Код, который вы указали в вопросе, заблокирован по какой-либо причине, когда уступил от background_task(), и вам не удалось получить результат await, потому что будущее не было торнадо будущего.

В немного отличающемся примечании, этот простой пример может быть легко реализован с использованием одного проекта thread/async и, скорее всего, ваш код также может быть выполнен без потоков. Темы легко внедряются, но одинаково легко ошибаться и могут привести к очень липким ситуациям. При попытке написать код с резьбой, пожалуйста, помните this photo :)