简体   繁体   English

龙卷风协程-自定义功能

[英]Tornado Coroutine - Custom function

I'm in the process of understanding coroutine from Tornado so let's keep everything simple and the more code you paste, the better. 我正在从Tornado理解协程,因此让我们保持所有简单,并且粘贴的代码越多越好。

What I want is to make my homemade function async. 我想要的是使我的自制函数异步。

All examples I could find in the documentation falls under the same "hidden" part: AsyncHTTPClient. 我在文档中可以找到的所有示例都在同一“隐藏”部分下:AsyncHTTPClient。 I'm not looking to do an HTTP call. 我不想进行HTTP呼叫。 So please do NOT give me example with that class. 因此,请勿在该课程中给我示例。 I'm interested to create something from scratch. 我有兴趣从头开始创建一些东西。 I've tried all possibilities on Tornado coroutine 我已经尝试过所有关于龙卷风协程的可能性

For now I've been testing with a bash sleep. 目前,我一直在进行bash睡眠测试。 Here is the code: 这是代码:

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()

But if I visit test1 then I need to wait for the call to return before I can access test2. 但是,如果我访问test1,则需要等待调用返回,然后才能访问test2。 From what I've understood, I need to use gen.sleep(5) . 据我了解,我需要使用gen.sleep(5) But that's just an example. 但这只是一个例子。 Let's say instead of running sleep 5 on bash, I'm running ssh somewhere 'do_something' which takes some time to run. 假设不是在bash上运行sleep 5 ,而是在ssh somewhere 'do_something'运行ssh somewhere 'do_something' ,这需要一些时间才能运行。

I've been told "this function isn't asynchronous". 有人告诉我“此函数不是异步的”。 So my question is how do I make a custom function asynchronous? 所以我的问题是如何使自定义函数异步?

EDIT: After searching a little, I've seen there is tornado.process https://gist.github.com/FZambia/5756470 to be used here. 编辑:经过一些搜索,我已经看到有tornado.process https://gist.github.com/FZambia/5756470在这里使用。 But my subprocess comes from a 3rd party, so it's not really something I can overwrite. 但是我的子流程来自第三方,所以实际上我无法覆盖。 So my question is also, how do I integrate 3rd party libraries with that gen.coroutine system? 所以我的问题也是,我如何将第三方库与该gen.coroutine系统集成?

SOLUTION: Thanks to the comments below I've got a 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()

I've asked a similar question here: Python Tornado - Confused how to convert a blocking function into a non-blocking function 我在这里问过类似的问题: Python Tornado-困惑如何将阻塞函数转换为非阻塞函数

The issue is that your function might be CPU bound, and the only way is to use an executor. 问题是您的函数可能受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)

Then you could do something like: 然后,您可以执行以下操作:

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

This task will be set aside, and run independently, since there is a yield keyword, tornado will do some other thing while doing a pure thread switch between what it is currently running and your process. 由于有一个yield关键字,因此该任务将被搁置并独立运行,龙卷风将在当前正在运行的进程与您的进程之间进行纯线程切换时执行其他操作。 It seems to be work fine for me. 对我来说似乎很好。

In other word, you can wrap the subprocess in the executor, and it will be processed asynchronously. 换句话说,您可以将子流程包装在执行程序中,它将被异步处理。

If you don't want to use an executor, it seems that your functions need be implemented in a state machine way. 如果您不想使用执行程序,似乎您的功能需要以状态机的方式实现。

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

Notice that Momoko (Postgres), and Motor (MongoDB), are all I/O Bound. 请注意,Momoko(Postgres)和Motor(MongoDB)都是I / O绑定的。

Edit: I'm not sure what are your uses for Tornado. 编辑:我不确定您对龙卷风有什么用。 I use Tornado when I do a lot of I/O, because I'm I/O bound. 当我执行大量I / O时,我会使用Tornado,因为我受I / O约束。 However, I guess if your uses are more CPU bound, you might want to look at Flask. 但是,我想如果您的使用受到CPU的更多限制,则可能需要查看Flask。 You can easily use Gunicorn and Flask to create something simple, and utilize multiple cores. 您可以轻松地使用Gunicorn和Flask创建简单的东西,并利用多个核心。 Trying to use multithread or multicore in Tornado can cause you a lot of headaches since a lot of things in Tornado are not thread safe. 在Tornado中尝试使用多线程或多核会引起很多麻烦,因为Tornado中的许多事情都不是线程安全的。

Edit 2: Removed the .result() call. 编辑2:删除了.result()调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM