2016-07-01 7 views
1

Ich bin dabei, Coroutine von Tornado zu verstehen, also lassen Sie uns alles einfach halten und je mehr Code Sie einfügen, desto besser.Tornado Coroutine - Benutzerdefinierte Funktion

Was ich will ist, meine hausgemachte Funktion async zu machen.

Alle Beispiele, die ich in der Dokumentation finden konnte, fallen unter den gleichen "versteckten" Teil: AsyncHTTPClient. Ich suche keinen HTTP-Anruf. Also bitte gib mir kein Beispiel mit dieser Klasse. Ich bin daran interessiert, etwas von Grund auf neu zu erstellen. Ich habe alle Möglichkeiten auf Tornado coroutine

ausprobiert Vorerst habe ich mit einem Bash-Schlaf getestet. Hier ist der 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() 

Aber wenn ich test1 besuchen dann brauche ich für den Anruf warten, um zurückzukehren, bevor ich test2 zugreifen können. Nach dem, was ich verstanden habe, muss ich gen.sleep(5) verwenden. Aber das ist nur ein Beispiel. Lassen Sie uns sagen, anstatt sleep 5 on bash laufen zu lassen, ich laufen das dauert einige Zeit zu laufen.

Mir wurde gesagt, "diese Funktion ist nicht asynchron". Also meine Frage ist, wie mache ich eine benutzerdefinierte Funktion asynchron?

EDIT: Nach ein wenig suchen, habe ich gesehen, es gibt Tornado.Process https://gist.github.com/FZambia/5756470 hier verwendet werden. Aber mein Subprozess kommt von einer dritten Partei, also ist es nicht wirklich etwas, das ich überschreiben kann. Also meine Frage ist auch, wie integriere ich 3rd-Party-Bibliotheken mit diesem gen.coroutine System?

LÖSUNG: Durch den Kommentaren unten ich eine Lösung haben:

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

Können Sie versuchen, die Datei .result() zu entfernen? Das war mein Fehler. Der Ertrag sollte automatisch zum Ergebnis führen. Möglicherweise warten Sie auf das Ergebnis() und es wird blockiert. – user1157751

Antwort

4

ich eine ähnliche Frage hier gefragt haben: Python Tornado - Confused how to convert a blocking function into a non-blocking function

Das Problem ist, dass Ihre Funktion werden CPU gebunden könnte, und der einzige Weg ist, einen Executor zu verwenden.

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) 

Dann könnten Sie so etwas wie:

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

Diese Aufgabe beiseite gesetzt werden, und unabhängig laufen, da es eine Ausbeute Schlüsselwort ist, Tornado wird eine andere Sache tun, während einen reinen Faden tun Wechseln Sie zwischen dem, was es gerade ausführt, und Ihrem Prozess. Es scheint mir gut zu funktionieren.

Mit anderen Worten, Sie können den Subprozess im Executor umbrechen, und es wird asynchron verarbeitet.

Wenn Sie keinen Executor verwenden möchten, müssen Ihre Funktionen auf eine State-Machine-Weise implementiert werden.

Ein weiterer Artikel: https://emptysqua.re/blog/motor-internals-how-i-asynchronized-a-synchronous-library/

Beachten Sie, dass Momoko (Postgres) und Motor (MongoDB), sind alle I/O-Bound.

Bearbeiten: Ich bin mir nicht sicher, was sind Ihre Anwendungen für Tornado. Ich benutze Tornado, wenn ich viel I/O mache, weil ich I/O gebunden bin. Allerdings, wenn Ihre Anwendungen mehr CPU-gebunden sind, möchten Sie vielleicht Flask betrachten. Sie können leicht Gunicorn und Flask verwenden, um etwas Einfaches zu erstellen und mehrere Kerne zu verwenden. Der Versuch, Multithread oder Multicore in Tornado zu verwenden, kann Ihnen viele Kopfschmerzen bereiten, da viele Dinge in Tornado nicht threadsicher sind.

Bearbeiten 2: Der Aufruf .result() wurde entfernt.

+0

Was würdest du in deine some_long_running_funktion einbauen? Ich habe meinen Test nach Ihrer Empfehlung geändert ... immer noch kein Glück (Update prüfen). – Regnoult

+0

Können Sie mir Sie Code zeigen? Eine lange laufende Funktion ist im Grunde der Funktionsname ohne die Klammern. Alle Variablen werden mit Kommas übergeben. – user1157751

+0

Das war es! Ich aktualisiere meine Frage, um die Lösung darin zu haben – Regnoult

Verwandte Themen