Python Tornado - 困惑如何将阻塞函数转换为非阻塞函数
Python Tornado - Confused how to convert a blocking function into a non-blocking function
假设我有一个 long 运行ning 函数:
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 编写异步库?
如果阻塞函数是 CPU 绑定的(如您的 for/xrange 示例),那么线程(或进程)是使其成为非阻塞的唯一方法。为每个传入请求创建一个线程是昂贵的,但是制作一个小的 ThreadPoolExecutor 来处理所有 CPU 绑定的操作并不昂贵。
要在不使用线程的情况下使函数成为非阻塞函数,该函数必须事件驱动:它必须等待某个外部事件(例如网络I/O) 这样它就可以在该事件发生时被唤醒。
我目前正在努力使用 Tornado 及其 WebSocket 函数为我的模拟程序添加 Web 界面。
我的模拟程序是计算密集型的,即 @ben-darnell 所说的 CPU-bound 任务,应该使用另一个 thread 或 process.
经过多次调查,我认为这些资源可能会有帮助:
- Tornado blocking asynchronous requests - Answer by @koblas
- Trying to call a long blocking function in tornado in a non blocking way
我现在正在做类似的实现,当我有更多进展时会更新这个答案:)
假设我有一个 long 运行ning 函数:
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 编写异步库?
如果阻塞函数是 CPU 绑定的(如您的 for/xrange 示例),那么线程(或进程)是使其成为非阻塞的唯一方法。为每个传入请求创建一个线程是昂贵的,但是制作一个小的 ThreadPoolExecutor 来处理所有 CPU 绑定的操作并不昂贵。
要在不使用线程的情况下使函数成为非阻塞函数,该函数必须事件驱动:它必须等待某个外部事件(例如网络I/O) 这样它就可以在该事件发生时被唤醒。
我目前正在努力使用 Tornado 及其 WebSocket 函数为我的模拟程序添加 Web 界面。 我的模拟程序是计算密集型的,即 @ben-darnell 所说的 CPU-bound 任务,应该使用另一个 thread 或 process.
经过多次调查,我认为这些资源可能会有帮助:
- Tornado blocking asynchronous requests - Answer by @koblas
- Trying to call a long blocking function in tornado in a non blocking way
我现在正在做类似的实现,当我有更多进展时会更新这个答案:)