Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在线程中使用Popen会阻止每个传入的SocketIO请求_Python_Multithreading_Flask_Pipe_Popen - Fatal编程技术网

Python 在线程中使用Popen会阻止每个传入的SocketIO请求

Python 在线程中使用Popen会阻止每个传入的SocketIO请求,python,multithreading,flask,pipe,popen,Python,Multithreading,Flask,Pipe,Popen,我有以下情况: 我在socketio服务器上收到一个请求。我接了。发射。。然后在另一个线程中启动一些计算量大的任务 如果繁重的计算是由subprocess.Popen使用subprocess.PIPE引起的,则只要传入的请求正在执行,它就会完全阻止,尽管它发生在单独的线程中 没问题-在中,建议异步读取缓冲区大小为1的子进程的结果,以便在这些读取之间,其他线程有机会执行某些操作。不幸的是,这对我没有帮助 我也已经使用了eventlet,只要我不在线程中使用subprocess.Popen和subp

我有以下情况: 我在socketio服务器上收到一个请求。我接了。发射。。然后在另一个线程中启动一些计算量大的任务

如果繁重的计算是由subprocess.Popen使用subprocess.PIPE引起的,则只要传入的请求正在执行,它就会完全阻止,尽管它发生在单独的线程中

没问题-在中,建议异步读取缓冲区大小为1的子进程的结果,以便在这些读取之间,其他线程有机会执行某些操作。不幸的是,这对我没有帮助

我也已经使用了eventlet,只要我不在线程中使用subprocess.Popen和subprocess.PIPE,它就可以正常工作

在这个代码示例中,您可以看到它只在subprocess.PIPE中使用subprocess.Popen时发生。当使用SimulatedHeavyLoad取消对函数的注释,而使用HeavyLoad取消对函数的注释时,一切都像charm一样工作

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