Python 在线程中使用Popen会阻止每个传入的SocketIO请求
我有以下情况: 我在socketio服务器上收到一个请求。我接了。发射。。然后在另一个线程中启动一些计算量大的任务 如果繁重的计算是由subprocess.Popen使用subprocess.PIPE引起的,则只要传入的请求正在执行,它就会完全阻止,尽管它发生在单独的线程中 没问题-在中,建议异步读取缓冲区大小为1的子进程的结果,以便在这些读取之间,其他线程有机会执行某些操作。不幸的是,这对我没有帮助 我也已经使用了eventlet,只要我不在线程中使用subprocess.Popen和subprocess.PIPE,它就可以正常工作 在这个代码示例中,您可以看到它只在subprocess.PIPE中使用subprocess.Popen时发生。当使用SimulatedHeavyLoad取消对函数的注释,而使用HeavyLoad取消对函数的注释时,一切都像charm一样工作Python 在线程中使用Popen会阻止每个传入的SocketIO请求,python,multithreading,flask,pipe,popen,Python,Multithreading,Flask,Pipe,Popen,我有以下情况: 我在socketio服务器上收到一个请求。我接了。发射。。然后在另一个线程中启动一些计算量大的任务 如果繁重的计算是由subprocess.Popen使用subprocess.PIPE引起的,则只要传入的请求正在执行,它就会完全阻止,尽管它发生在单独的线程中 没问题-在中,建议异步读取缓冲区大小为1的子进程的结果,以便在这些读取之间,其他线程有机会执行某些操作。不幸的是,这对我没有帮助 我也已经使用了eventlet,只要我不在线程中使用subprocess.Popen和subp
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)
在functionWithHeavyLoad函数中的阻塞工作完成后,客户端收到消息“foo”。不过,它应该更早地收到消息
此示例可以复制并粘贴到.py文件中,行为可以立即复制
我使用的是Python 3.4.3、Flask 0.10.1、Flask-socketio1.2、eventlet 0.17.4
更新
如果我将其放入函数WithHeavyload函数中,它实际上可以工作,并且一切正常:
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()
问题是:我使用find进行重载,以便使您的样本更易于复制。然而,在我的代码中,我实际上使用了tesseract{0}stdout-ldeu作为sell命令。这个发现仍然阻止了一切。这是tesseract问题而不是eventlet问题吗?但是:如果它发生在一个单独的线程中,当find不阻塞时,它会通过上下文切换逐行读取,那么它怎么会阻塞呢?多亏了这个问题,我今天学到了一些新东西。Eventlet确实提供了一个对greenlet友好的子流程及其函数版本,但由于一些奇怪的原因,它没有在标准库中对该模块进行猴补丁 链接到子流程的eventlet实现: 查看eventlet,修补的模块包括操作系统、选择、套接字、线程、时间、MySQLdb、内置和psycopg2。补丁程序中绝对没有对子进程的引用 好消息是,在我替换了以下内容后,我能够在与您非常相似的应用程序中使用Popen:
import subprocess
与:
但是请注意,当前发布的eventlet 0.17.4版本不支持Popen中的universal_新行选项,如果使用它,您将收到一个错误。此选项的支持在master中,此处是添加此选项的。您必须从调用中删除该选项,或者从github安装eventlet direct的主分支。您对的解释不正确。bufsize=1表示缓存了行,对其他线程没有影响。更重要的是,在真实的情况下使用它是毫无意义的:q.get_nowait。这里不需要使用嵌套线程。Try:text=os.fsdecodesubprocess.check\u outputfind/test.split,stdin=DEVNULL,stderr=DEVNULL这里的关键是避免从父级继承标准流。eventlet.monkey_是否修补了time.sleep、threading.Thread?如果我使用recognizedText=os.fsdecodesubprocess.check_outputshellCommand,stdin=os.devnull,stderr=os.devnull代替Popen,q.get_nowait在while循环和线程中,就像你说的,在整个过程中,一切都被阻塞了,就像以前一样。1。stdin=os.devnull无法配置系统,若要查看错误,请改用subprocess.devnull 2。如果它阻塞了所有内容,尽管它是从运行在不同线程中的testThreadFunction调用的,那么您应该研究eventlet.tesseract和find block on.readline中的threading+子进程的替代方案。区别在于find可能会更快地刷新其标准输出缓冲区。尝试任何不经常刷新其标准输出缓冲区的命令,查看它是否会阻塞,例如[time.sleep1]:print python在非交互模式下启用块缓冲模式,因此不会在换行时刷新缓冲区。所有东西都会阻塞,直到缓冲区在一小时内溢出约4K行。请使用未为eventlet修补的真实线程,或者使用希望与eventlet IO一起工作的子进程替代方案,因为它与事件循环集成在一起。感谢您的帮助。这立刻奏效了。他们没有修补子流程模块,这有点令人失望。这正是我所期待的。是的,我计划在有点空闲的时候提交一个补丁,将子进程添加到补丁程序中。
from eventlet.green import subprocess