假设我有一个长时间运行的功能:
def long_running_function():
result_future = Future()
result = 0
for i in xrange(500000):
result += i
result_future.set_result(result)
return result_future
我在一个处理程序中有一个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"
如果我同时在两个网络浏览器上运行上述代码,我会得到:
启动
端
启动
端
这似乎阻止了。根据我的理解,@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)
这也没有削减,它给了我:
启动
端
启动
端
我可以使用线程并行执行那些,但它似乎没有办法,因为我可能会打开很多线程,根据Tornado的用户指南,它可能很昂贵。
人们如何为Tornado编写异步库?
答案 0 :(得分:8)
如果阻塞函数受CPU约束(正如for / xrange示例所示),则线程(或进程)是使其无阻塞的唯一方法。为每个传入的请求创建一个线程是很昂贵的,但是制作一个小的ThreadPoolExecutor来处理所有CPU绑定的操作不是。
要在不使用线程的情况下使函数无阻塞,该函数必须是事件驱动的:它必须等待某些外部事件(例如网络I / O),以便它可以当事件发生时醒来。
答案 1 :(得分:0)
I'm currently struggling to add a web interface for my simulation program using Tornado and its WebSocket function. My simulation program is computationally intensive, i.e., CPU-bound task as said by @ben-darnell , which should be implemented using another thread or process.
After many investigations, I think these resources may be helpful:
I'm doing the similar implementation now, and will update this answer when I have more progress :)