2016-07-01 45 views
1

我正在理解龙卷风的协程,所以让我们保持一切简单,粘贴的代码越多越好。龙卷风协程 - 自定义函数

我想要的是使我的自制函数异步。

我可以在文档中找到的所有示例均属于相同的“隐藏”部分:AsyncHTTPClient。我不打算做一个HTTP调用。所以请不要给我那个班的例子。我有兴趣从头开始创造一些东西。我试过所有的可能性Tornado coroutine

现在我已经测试了bash睡眠。下面是代码:

import tornado.web 
import tornado.httpserver 
import tornado.gen 
import tornado.concurrent 
import subprocess 
import os 

@tornado.gen.coroutine 
def letswait(): 
    fut = tornado.concurrent.Future() 
    subprocess.check_output(["sleep", "5"]) 
    fut.set_result(42) 
    return fut 

class TestHandler1(tornado.web.RequestHandler): 
    @tornado.gen.coroutine 
    def get(self): 
     value = yield letswait() 
     self.render("test.html", num=value) 

class TestHandler2(tornado.web.RequestHandler): 
    def get(self): 
     self.render("test.html", num=66) 

class Application(tornado.web.Application): 
    def __init__(self): 
     DIRNAME = os.path.dirname(__file__) 
     STATIC_PATH = os.path.join(DIRNAME, '../static') 
     TEMPLATE_PATH = os.path.join(DIRNAME, '../template') 
     sets = { 
      "template_path":TEMPLATE_PATH, 
      "static_path":STATIC_PATH, 
      "debug":True, 
     } 
     tornado.web.Application.__init__(self, [ 
      (r"/test1", TestHandler1), 
      (r"/test2", TestHandler2), 
     ], **sets) 

def main(): 
    http_server = tornado.httpserver.HTTPServer(Application()) 
    http_server.listen(8888) 
    print "Let s start" 
    tornado.ioloop.IOLoop.instance().start() 

if __name__ == "__main__": 
    main() 

但是如果我访问test1的话,我需要等待调用返回之前,我可以访问test2的。根据我的理解,我需要使用gen.sleep(5)。但这只是一个例子。让我们说,而不是在bash上运行sleep 5,我正在运行ssh somewhere 'do_something',这需要一些时间来运行。

我被告知“这个函数不是异步的”。所以我的问题是我如何使自定义函数异步?

编辑:搜索了一下后,我看到有在这里使用的tornado.process https://gist.github.com/FZambia/5756470。但是我的子进程来自第三方,所以它不是我可以覆盖的东西。所以我的问题是,如何将第三方库与gen.coroutine系统集成?

SOLUTION:多亏了下面的评论我已经有了一个解决方案:

import tornado.web 
import tornado.httpserver 
import tornado.gen 
import tornado.concurrent 
import subprocess 
import os 

from concurrent import futures 

# Create a threadpool, and this can be shared around different python files 
# which will not re-create 10 threadpools when we call it. 
# we can a handful of executors for running synchronous tasks 

# Create a 10 thread threadpool that we can use to call any synchronous/blocking functions 
executor = futures.ThreadPoolExecutor(10) 

def letswait(): 
    result_future = tornado.concurrent.Future() 
    subprocess.check_output(["sleep", "5"]) 
    result_future.set_result(42) 
    return result_future 

class TestHandler1(tornado.web.RequestHandler): 
    @tornado.gen.coroutine 
    def get(self): 
     value = yield executor.submit(letswait) 
     self.render("test.html", num=value) 

class TestHandler2(tornado.web.RequestHandler): 
    def get(self): 
     self.render("test.html", num=66) 

class Application(tornado.web.Application): 
    def __init__(self): 
     DIRNAME = os.path.dirname(__file__) 
     STATIC_PATH = os.path.join(DIRNAME, '../static') 
     TEMPLATE_PATH = os.path.join(DIRNAME, '../template') 
     sets = { 
      "template_path":TEMPLATE_PATH, 
      "static_path":STATIC_PATH, 
      "debug":True, 
     } 
     tornado.web.Application.__init__(self, [ 
      (r"/test1", TestHandler1), 
      (r"/test2", TestHandler2), 
     ], **sets) 

def main(): 
    http_server = tornado.httpserver.HTTPServer(Application()) 
    http_server.listen(8888) 
    print "Let s start" 
    tornado.ioloop.IOLoop.instance().start() 

if __name__ == "__main__": 
    main() 
+0

你可以尝试删除.result()?那是我的错误。收益率应该自动得到结果。有可能你正在等待结果(),并且它会变成阻塞状态。 – user1157751

回答

4

我已经问过类似的问题在这里:Python Tornado - Confused how to convert a blocking function into a non-blocking function

的问题是,你的功能可能会受CPU限制,唯一的办法是使用执行者。

from concurrent import futures 

# Create a threadpool, and this can be shared around different python files 
# which will not re-create 10 threadpools when we call it. 
# we can a handful of executors for running synchronous tasks 

# Create a 10 thread threadpool that we can use to call any synchronous/blocking functions 
executor = futures.ThreadPoolExecutor(10) 

然后,你可以这样做:

@gen.coroutine 
def get(self): 
    json = yield executor.submit(some_long_running_function) 

此任务将被作废,并独立运行,因为有yield关键字,龙卷风会做一些其他的事情,而做一个纯粹的线程在当前正在运行的程序和您的程序之间切换。这对我来说似乎很好。

换句话说,你可以将子过程包装在执行器中,并且它将被异步处理。

如果你不想使用执行程序,看起来你的功能需要以状态机的方式来实现。

另一篇文章:https://emptysqua.re/blog/motor-internals-how-i-asynchronized-a-synchronous-library/

注意,桃子(Postgres的),和马达(MongoDB的),是所有I/O约束。

编辑: 我不确定你有什么用途Tornado。当我做很多I/O时,我使用Tornado,因为我是I/O绑定的。不过,我想如果你的使用更受CPU限制,你可能想看看Flask。您可以轻松使用Gunicorn和Flask来创建简单的东西,并利用多个核心。尝试在Tornado中使用多线程或多核可能会导致很多令人头疼的问题,因为Tornado中的很多内容都不是线程安全的。

编辑2:删除.result()调用。

+0

你会把什么放入你的some_long_running_function?我按照你的建议改变了我的测试...仍然没有运气(检查更新)。 – Regnoult

+0

你能告诉我你我的代码吗?长时间运行的函数基本上是没有括号的函数名称。任何变量都以逗号传递。 – user1157751

+0

就是这样!我正在更新我的问题,让其中的解决方案 – Regnoult