假设我有一个长时间运行的功能:
def long_running_function():
result_future = Future()
result = 0
for i in xrange(500000):
result += i
result_future.set_result(result)
return result_future
Run Code Online (Sandbox Code Playgroud)
我在一个处理程序中有一个get函数,它使用for循环的上述结果打印用户,该循环将添加xrange中的所有数字:
@gen.coroutine
def get(self):
print "start"
self.future = long_running_function()
message = yield self.future
self.write(str(message))
print "end"
Run Code Online (Sandbox Code Playgroud)
如果我同时在两个Web浏览器上运行上面的代码,我得到:
开始
结束
开始
结束
这似乎是封锁的.根据我的理解,@gen.coroutine和yield语句不会阻止get函数中的IOLoop,但是,如果任何函数在阻塞的协同例程中,那么它会阻塞IOLoop.
因此,我做的另一件事是将其long_running_function转换为回调,并使用yield gen.Task替代.
@gen.coroutine
def get(self):
print "start"
self.future = self.long_running_function
message = yield gen.Task(self.future, None)
self.write(str(message))
print "end"
def long_running_function(self, arguments, callback):
result = 0
for i in xrange(50000000):
result += i
return callback(result)
Run Code Online (Sandbox Code Playgroud)
这也没有削减,它给了我:
开始
结束
开始
结束
我可以使用线程并行执行那些,但它似乎没有办法,因为我可能会打开很多线程,根据Tornado的用户指南,它可能很昂贵.
人们如何为Tornado编写异步库?
如果阻塞函数受CPU约束(正如for/xrange示例所示),则线程(或进程)是使其无阻塞的唯一方法.为每个传入请求创建一个线程是昂贵的,但是使用一个小的ThreadPoolExecutor来处理所有CPU绑定的操作则不然.
要在不使用线程的情况下使函数无阻塞,该函数必须是事件驱动的:它必须等待某些外部事件(例如网络I/O),以便在该事件发生时唤醒它.