为什么我的协同程序块整个龙卷风实例? [英] why my coroutine blocks whole tornado instance?

查看:158
本文介绍了为什么我的协同程序块整个龙卷风实例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

from tornado import web, gen
import tornado, time

class CoroutineFactorialHandler(web.RequestHandler):
    @web.asynchronous
    @gen.coroutine
    def get(self, n, *args, **kwargs):
        n = int(n)
        def callbacker(iterator, callback):
            try:
                value = next(iterator)
            except StopIteration:
                value = StopIteration
            callback(value)

        def factorial(n):
            x = 1
            for i in range(1, n+1):
                x *= i
                yield

            yield x

        iterator = factorial(n)
        t = time.time()
        self.set_header("Content-Type", "text/plain")
        while True:
            response = yield gen.Task(callbacker, iterator)
            #log.debug("response: %r" %response)
            if response is StopIteration:
                break
            elif response:
                self.write("took : %f sec" %(time.time() - t))
                self.write("\n")
                self.write("f(%d) = %d" %(n, response))

        self.finish()

application = tornado.web.Application([
    (r"^/coroutine/factorial/(?P<n>\d+)", CoroutineFactorialHandler),
    #http://localhost:8888/coroutine/factorial/<int:n>
])

if __name__ == "__main__":
    application.listen(8888)
    ioloop = tornado.ioloop.IOLoop.instance()
    ioloop.start()

21行猛拉
以上就是简单的阶乘计算器。它循环N次,在发电机的方式。

21 lines yanked above is the simple factorial calculator. it loops N times, in generator fashion.

问题是,当这种code正在执行它阻止了整个龙卷风。

the problem is, when this code is executing it blocks the whole tornado.

我要实现的是写龙卷风了一些辅助的治疗发生器作为协程,因此可以服务于异步方式的要求是什么。
(我已阅读<一个href=\"http://stackoverflow.com/questions/8812715/using-a-simple-python-generator-as-a-co-routine-in-a-tornado-async-handler?rq=1\">Using一个简单的Python发生器作为龙卷风异步处理程序协同例程?)

what I want to achieve is writing some helper for tornado that treats generators as coroutine, and therefore can serve requests in asynchronous manner. (I have read Using a simple python generator as a co-routine in a Tornado async handler?)

为什么简单的增加和乘乘n循环块,整个龙卷风?

why does the simple increase-and-multiply-by-n loop block the whole tornado?

编辑:我编辑了code到包括整个应用程序,您可以运行并测试它。
我在Python 2.7版运行龙卷风3.1.1

edit : I edited the code to include the whole application, that you can run and test it. I'm running tornado 3.1.1 on python 2.7

推荐答案

您必须记住,龙卷风在一个线程中运行。的code被分成任务被调用顺序中主循环。如果这些任务之一,需要很长的完成(因为阻塞功能,如 time.sleep的()或一些繁重的计算阶乘一样),它会阻止整个循环结果。

You have to remember that Tornado runs in one thread. The code is split into task that are called sequentially in main loop. If one of these task takes long to finish (because of blocking functions like time.sleep() or some heavy computation like factorial) it will block entire loop as a result.

所以,你可以做什么......?一种解决方案是使用创建循环 IOLoop.add_callback()

So what you can do...? One solution is to create loop using IOLoop.add_callback():

from tornado import web, gen
import tornado, time

class CoroutineFactorialHandler(web.RequestHandler):
    def factorial(self, limit=1):
        count = 1
        fact = 1
        while count <= limit:
            yield fact
            count = count + 1
            fact = fact * count 

    def loop(self):
        try:
            self.fact = self.generator.next()
            tornado.ioloop.IOLoop.instance().add_callback(self.loop)
        except StopIteration:
            self.write("took : %f sec" %(time.time() - self.t))
            self.write("\n")
            self.write("f(%d) = %d" % (self.n, self.fact))
            self.finish()

    @web.asynchronous
    def get(self, n, *args, **kwargs):
        self.n = int(n)
        self.generator = self.factorial(self.n)
        self.t = time.time()
        self.set_header("Content-Type", "text/plain")
        tornado.ioloop.IOLoop.instance().add_callback(self.loop)

application = tornado.web.Application([
    (r"^/coroutine/factorial/(?P<n>\d+)", CoroutineFactorialHandler),
    #http://localhost:8888/coroutine/factorial/<int:n>
])

if __name__ == "__main__":
    application.listen(8888)
    ioloop = tornado.ioloop.IOLoop.instance()
    ioloop.start()

每一个乘法是这里一个单独的任务,它允许混合阶乘生成不同的请求调用。这是一个很好的方法,如果要生成每次调用了时间相同。但是,如果你将被计算100000!然后在序列时间任务的一些点将看起来像90000!* 90001,90001!* 90002等。它需要一定的时间,以具有此计算,即使其仅一次乘法,而不是整个循环因此其他请求将被延迟。对于这样大的输入整数,你不得不在另一个线程的计算有处理器时间公平份额的请求。我这里是如何做到这一点:<一href=\"http://lbolla.info/blog/2013/01/22/blocking-tornado\">http://lbolla.info/blog/2013/01/22/blocking-tornado

Every multiplication is a separate task here, which allows mixing factorial generator calls from different requests. This is a good approach if every call to generator took same amount of time. However if you will be computing 100000! then at some point in time tasks in sequence will be looking like 90000!*90001, 90001!*90002 and so on. It takes some time to have this computed even if its only one multiplication instead of whole loop so the other request will be delayed. For such big input integer you have to make computations in another thread to have fair share of processor time for a request. Here is example how to do this: http://lbolla.info/blog/2013/01/22/blocking-tornado

作为一个方面说明,在阶乘你有很多冗余的,所以你应该在内存中的保留解决方案列表对于某个n把他们实时的而不是一遍一遍浪费处理器时间相同的计算。

As a side note, in factorial you have a lot of redundancy so you should keep list of solutions for some n at memory to turn them back instantly without wasting processor time for same computation over and over again.

这篇关于为什么我的协同程序块整个龙卷风实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆