我的Web应用程序中需要异步子进程锁。 我写下一个代码:
r = redis.Redis('localhost')
pipe = r.pipeline()
is_locked = False
while not is_locked:
try:
pipe.watch(lock_name)
current_locked = int(pipe.get(lock_name))
if current_locked == 0:
pipe.multi()
pipe.incr(lock_name)
pipe.execute()
is_locked = True
else:
yield None
except redis.WatchError:
yield None
return True
在编写的文档中,tornado.gen.moment(版本4.5以来的yield None
)是一个特殊的对象,可以让IOLoop运行一次迭代。这个怎么运作?是否与其他Feature对象(来自其他请求)进行下一次迭代?这是正确的yield None
用法吗?
答案 0 :(得分:2)
gen.moment
刚刚解决了使用回调添加到ioloop的Future对象。这允许运行ioloop的一次迭代。
使用协程的gen.Runner中的convert_yielded
将yield None
转换为gen.moment
。
每次迭代的ioloop(基本上是while True
)都会执行以下操作:
使用ioloop的add_callback
或add_callback_from_signal
使用ioloop的add_timeout
轮询fd事件(例如,等待文件descirptor准备好写入或读取)。当然,为了不阻止ioloop,民意调查已超时。
运行ready fds的处理程序
因此,到达yield gen.moment
点将允许一次完成上述所有事情(一次迭代)。
作为示例,让我们安排异步任务 - 需要运行ioloop的httpclient fetch。另一方面,还会有阻塞功能(time.sleep
)。
import time
from tornado import gen
from tornado.ioloop import IOLoop
from tornado.httpclient import AsyncHTTPClient
@gen.coroutine
def fetch_task():
client = AsyncHTTPClient()
yield client.fetch('http://google.com')
print('fetch_task finished')
@gen.coroutine
def blocking():
start_time = time.time()
counter = 1
while True:
time.sleep(5)
print('blocking for %f' % (time.time() - start_time))
yield gen.moment
print('gen.moment counter %d' % counter)
counter += 1
@gen.coroutine
def main():
fetch_task()
yield blocking()
IOLoop.instance().run_sync(main)
观察:
yield gen.moment
,fetch_task
将无法完成time.sleep
的增加/减少值不会影响完成fetch_task
的ioloop所需的迭代次数。这也意味着AsyncHTTPClient.fetch
是N + 1
(gen.moments +任务计划)与ioloop的交互(处理回调,轮询fd,处理事件)。gen.moment
并不总是意味着,其他任务将完成,而是让他们有机会更接近完成。