我正在尝试编写一个用于 Tornado 应用程序的异步方法。我的方法需要管理一个连接,该连接可以并且应该在对该函数的其他调用之间共享连接由 await
ing 创建。为了解决这个问题,我使用了 asyncio.Lock
。但是,每次调用我的方法都会挂起等待锁定。
经过几个小时的实验,我发现了一些事情,
- 如果锁 block 中没有
await
,一切都按预期工作 tornado.ioloop.IOLoop.configure('tornado.platform.asyncio.AsyncIOLoop')
没有帮助tornado.platform.asyncio.AsyncIOMainLoop().install()
允许它工作,无论事件循环是否以tornado.ioloop.IOLoop.current().start 开始()
或asyncio.get_event_loop().run_forever()
这是一些示例代码,除非您取消注释 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 事件循环并使用它。asyncio 循环成为单例时IOLoop 已启动。这几乎 有效,但是当定义了 MainHandler
类(并创建其类范围的 asyncio.Lock
时,asyncio singleton 仍然指向默认值(它将被 AsyncIOLoop 创建的那个取代)。
TL;DR:使用 AsyncIOMainLoop,而不是 AsyncIOLoop,除非您尝试使用更高级的非单例使用模式。这在 Tornado 5.0 中会变得更简单,因为异步集成将默认启用。
关于python - Tornado 应用程序中的 asyncio.lock,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46479019/