【问题标题】:Python tornado gen.coroutine blocks requestPython tornado gen.coroutine 阻止请求
【发布时间】:2016-10-16 12:28:21
【问题描述】:

我是龙卷风和蟒蛇的新手。几天前我开始写一个非阻塞的rest api,但我还不能完成任务。当我同时向此端点“localhost:8080/async”发送两个请求时,第二个请求会在 20 秒后响应!这说明我做错了什么。

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()

【问题讨论】:

    标签: python tornado python-3.5


    【解决方案1】:

    切勿在 Tornado 代码中使用 time.sleep!使用 IOLoop.add_timeout 来安排稍后的回调,或在协程中yield gen.sleep(n)

    http://www.tornadoweb.org/en/latest/faq.html#why-isn-t-this-example-with-time-sleep-running-in-parallel

    【讨论】:

      【解决方案2】:

      这很奇怪,返回 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 结果,因为未来不是龙卷风的未来。

      稍有不同的是,这个简单的示例可以使用单线程/异步设计轻松实现,并且您的代码也可以在没有线程的情况下完成。线程很容易实现,但同样容易出错,并且可能导致非常棘手的情况。尝试编写线程代码时,请记住this photo :)

      【讨论】:

        猜你喜欢
        • 2014-07-29
        • 2013-11-12
        • 2012-10-14
        • 2021-08-28
        • 1970-01-01
        • 2020-07-10
        • 2016-06-12
        • 1970-01-01
        • 2021-01-05
        相关资源
        最近更新 更多