Python Tornado - 如何将阻塞函数转换为非阻塞函数?

9
假设我有一个耗时很长的函数:
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函数,它打印出使用xrange中所有数字的for循环的上述结果的用户。
@gen.coroutine
def get(self):
    print "start"

    self.future = long_running_function()
    message = yield self.future
    self.write(str(message))

    print "end"

如果我同时在两个Web浏览器上运行以上代码,我会得到:
开始
结束
开始
结束
这似乎是阻塞的。据我理解,@gen.coroutineyield语句并不会阻塞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 编写异步库?

2个回答

9
如果阻塞函数受限于CPU(就像您的for/xrange示例),那么线程(或进程)是使其非阻塞的唯一方法。为每个传入请求创建一个线程很昂贵,但创建一个小的ThreadPoolExecutor来处理所有CPU受限操作则不是。
如果不使用线程使函数非阻塞,该函数必须是事件驱动的:它必须等待某些外部事件(比如网络I/O),以便当事件发生时可以被唤醒。

我认为你是对的,但是调用阻塞函数并没有提到CPU绑定或事件驱动,它只是说:“从协程调用阻塞函数的最简单方法是使用ThreadPoolExecutor,它返回与协程兼容的Futures”。 - Cloud

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接