asyncio.lock 在 Tornado 应用程序中

asyncio.lock in a Tornado application

我正在尝试编写用于 Tornado 应用程序的异步方法。我的方法需要管理一个连接,该连接可以而且应该在对该函数的其他调用之间共享连接由 awaiting 创建。为了解决这个问题,我使用了 asyncio.Lock。但是,每次调用我的方法都会挂起等待锁定。

经过几个小时的实验,我发现了一些东西,

  1. 如果锁块中没有 await,则一切正常
  2. tornado.ioloop.IOLoop.configure('tornado.platform.asyncio.AsyncIOLoop')没有帮助
  3. tornado.platform.asyncio.AsyncIOMainLoop().install() 允许它工作,无论事件循环是从 tornado.ioloop.IOLoop.current().start() 还是 asyncio.get_event_loop().run_forever()
  4. 开始的

这是一些示例代码,除非您取消注释 AsyncIOMainLoop().install():

,否则它们将无法工作
import tornado.ioloop
import tornado.web
import tornado.gen
import tornado.httpclient
from tornado.platform.asyncio import AsyncIOMainLoop
import asyncio
import tornado.locks


class MainHandler(tornado.web.RequestHandler):

    _lock = asyncio.Lock()
    #_lock = tornado.locks.Lock()

    async def get(self):
        print("in get")
        r = await tornado.gen.multi([self.foo(str(i)) for i in range(2)])
        self.write('\n'.join(r))

    async def foo(self, i):
        print("Getting first lock on " + i)
        async with self._lock:
            print("Got first lock on " + i)
            # Do something sensitive that awaits
            await asyncio.sleep(0)
        print("Unlocked on " + i)

        # Do some work
        print("Work on " + i)
        await asyncio.sleep(0)

        print("Getting second lock on " + i)
        async with self._lock:
            print("Got second lock on " + i)
            # Do something sensitive that doesnt await
            pass
        print("Unlocked on " + i)
        return "done"


def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    #AsyncIOMainLoop().install()  # This will make it work
    #tornado.ioloop.IOLoop.configure('tornado.platform.asyncio.AsyncIOLoop')  # Does not help
    app = make_app()
    app.listen(8888)
    print('starting app')
    tornado.ioloop.IOLoop.current().start()

我现在知道 tornado.locks.Lock() 存在并且有效,但我很好奇为什么 asyncio.Lock 不起作用。

Tornado 和 asyncio 都有一个全局单例事件循环,其他一切都依赖于它(对于高级用例,您可以避免单例,但使用它是惯用的)。要一起使用这两个库,两个单例需要相互了解。

AsyncIOMainLoop().install() 创建一个指向 asyncio 单例的 Tornado 事件循环,然后将其设置为 Tornado 单例。这行得通。

IOLoop.configure('AsyncIOLoop') 告诉 Tornado“每当你需要 IOLoop 时,创建一个新的(非单例!)asyncio 事件循环并使用它。当 IOLoop 启动时,asyncio 循环变成单例。这个 almost 有效,但是当 MainHandler class 被定义(并创建它的 class-scoped asyncio.Lock,asyncio 单例仍然指向默认值(将替换为 AsyncIOLoop 创建的默认值)。

TL;DR:使用 AsyncIOMainLoop,而不是 AsyncIOLoop,除非您尝试使用更高级的非单例使用模式。这在 Tornado 5.0 中会变得更简单,因为异步集成将默认启用。