Mit Popen in einem Thread blockiert jede eingehende Flask-SocketIO-Anforderung

Ich habe folgende Situation: Ich erhalte eine Anfrage auf einem Socketio Server. Ich antworte es (socket.emit (..)) und dann etwas mit schwerer Berechnungslast in einem anderen Thread beginnen .

Wenn die schwere Berechnung durch subprocess.Popen (using subprocess.PIPE ) verursacht wird, blockiert es jede eingehende Anforderung, solange sie ausgeführt wird, obwohl es in einem separaten Thread passiert.

Kein Problem – in diesem Thread wurde vorgeschlagen, das Ergebnis des Teilprozesses asynchron mit einer Puffergröße von 1 zu lesen, so dass zwischen diesen Lügen andere Threads die Chance haben, etwas zu tun. Leider hat das für mich nicht geholfen

Ich habe auch schon monkeypatched eventlet und das funktioniert gut – solange ich keine subprocess.Popen mit subprocess.PIPE im Thread verwenden.

In diesem Codebeispiel kannst du sehen, dass es nur mit subprocess.Popen mit subprocess.PIPE passiert. Wenn Sie #functionWithSimulatedHeavyLoad() und stattdessen die functionWithHeavyLoad() kommentieren, functionWithHeavyLoad() alles wie Charme.

 from flask import Flask from flask.ext.socketio import SocketIO, emit import eventlet eventlet.monkey_patch() app = Flask(__name__) socketio = SocketIO(app) import time from threading import Thread @socketio.on('client command') def response(data, type = None, nonce = None): socketio.emit('client response', ['foo']) thread = Thread(target = testThreadFunction) thread.daemon = True thread.start() def testThreadFunction(): #functionWithSimulatedHeavyLoad() functionWithHeavyLoad() def functionWithSimulatedHeavyLoad(): time.sleep(5) def functionWithHeavyLoad(): from datetime import datetime import subprocess import sys from queue import Queue, Empty ON_POSIX = 'posix' in sys.builtin_module_names def enqueueOutput(out, queue): for line in iter(out.readline, b''): if line == '': break queue.put(line) out.close() # just anything that takes long to be computed shellCommand = 'find / test' p = subprocess.Popen(shellCommand, universal_newlines=True, shell=True, stdout=subprocess.PIPE, bufsize=1, close_fds=ON_POSIX) q = Queue() t = Thread(target = enqueueOutput, args = (p.stdout, q)) t.daemon = True t.start() t.join() text = '' while True: try: line = q.get_nowait() text += line print(line) except Empty: break socketio.emit('client response', {'text': text}) socketio.run(app) 

Der Client erhält die Nachricht 'foo', nachdem die Sperrarbeit in der Funktion functionWithHeavyLoad () abgeschlossen ist. Es sollte die Nachricht schon früher erhalten.

Dieses Beispiel kann kopiert und in eine .py-Datei eingefügt werden und das Verhalten kann sofort reproduziert werden.

Ich benutze Python 3.4.3, Flasche 0.10.1, Flasche-Socketio1.2, Eventlet 0.17.4

Aktualisieren

Wenn ich das in die functionWithHeavyLoad-Funktion stecke, funktioniert es eigentlich und alles ist gut:

 import shlex shellCommand = shlex.split('find / test') popen = subprocess.Popen(shellCommand, stdout=subprocess.PIPE) lines_iterator = iter(popen.stdout.readline, b"") for line in lines_iterator: print(line) eventlet.sleep() 

Das Problem ist: Ich habe für schwere Last gefunden, um die Probe für Sie leichter reproduzierbar zu machen. Allerdings, in meinem Code verwende ich eigentlich tesseract "{0}" stdout -l deu als den Verkaufsbefehl. Diese (im Gegensatz zu find ) blockiert immer noch alles. Ist das eher ein tesseraktes Problem als ein Ereignis? Aber noch: wie kann dies blockieren, wenn es in einem separaten Thread passiert, wo es Zeile für Zeile mit Kontext-Schalter liest, wenn der find nicht blockiert?

One Solution collect form web for “Mit Popen in einem Thread blockiert jede eingehende Flask-SocketIO-Anforderung”

Dank dieser Frage habe ich heute etwas Neues gelernt. Eventlet bietet eine Greenlet-freundliche Version von Subprocess und seine Funktionen, aber aus irgendeinem seltsamen Grund es nicht Affen Patch dieses Modul in der Standard-Bibliothek.

Link zur Eventlet-Implementierung des Unterprozesses: https://github.com/eventlet/eventlet/blob/master/eventlet/green/subprocess.py

Beim Betrachten des Eventlet- Patchers sind die Module, die gepatcht sind, os, select, socket, thread, time, MySQLdb, builtins und psycopg2. Es gibt absolut keinen Hinweis auf Unterprozeß im Patcher.

Die gute Nachricht ist, dass ich mit Popen() in einer Anwendung sehr ähnlich zu deinem arbeiten konnte, nachdem ich ersetzt hatte:

 import subprocess 

mit:

 from eventlet.green import subprocess 

Aber beachten Sie, dass die aktuell veröffentlichte Version von eventlet (0.17.4) nicht die universal_newlines Option in Popen , erhalten Sie einen Fehler, wenn Sie es verwenden. Unterstützung für diese Option ist im Master (hier ist das Commit , das die Option hinzugefügt hat). Sie müssen diese Option entweder aus Ihrem Anruf entfernen oder den Master-Zweig der Veranstaltung direkt aus Github installieren.

Python ist die beste Programmiersprache der Welt.