首页 > 解决方案 > 从greenlet中的标准输出非阻塞读取

问题描述

我有一个带有 SocketIO 的烧瓶应用程序。因此,我选择使用该eventlet库运行应用程序。如果我没记错的话,在后台,eventlet 使用绿色线程来实现并发。

在我的应用程序中,我想生成一个进程并通过 Web 套接字流式传输输出。下面是一个玩具示例

# eventlet==0.25.1
# flask==1.1.1
# flask-socketio==4.2.1

import eventlet
import subprocess
from flask import Flask, jsonify
from flask_socketio import SocketIO

app = Flask(__name__)
socketio = SocketIO(app)
socketio.init_app(app, cors_allowed_origins='*')

@app.route('/ping')
def start_ping():
    eventlet.spawn_n(ping)
    return jsonify(success=True)


@app.route('/hello')
def hello():
    return jsonify(data='Hello World')


def ping():
    proc = subprocess.Popen(
        ('ping', '-t', 'google.com'),
        stdout=subprocess.PIPE,
    )
    for line in proc.stdout:
        eventlet.sleep(0.5)
        socketio.emit('log_receive', str(line))


if __name__ == '__main__':
    socketio.run(app)
  1. 用户点击/ping端点。
  2. ping()函数在一个绿色线程中执行,该线程在子进程中运行 ping 命令
  3. emited从子进程的标准输出和通过网络套接字读取行
  4. eventlet.sleep(0.5)用于让应用程序的其他部分有机会运行。

问题:

for line in proc.stdout:正在阻塞。直到有东西通过标准输出,eventlet.sleep(0.5)才会执行,因此应用程序的其余部分没有机会运行。因此呈现应用程序,无响应。

我遇到了这个关于如何进行非阻塞读取的问题subprocess.PIPE,建议基本上是使用单独的线程来进行读取。

不幸的是,由于 eventlet/greenlet 限制了并发/协程编程模型,我无法使用单独的线程。

我可以使用fcntl()进行非阻塞读取,但我在 Windows 上,所以它不是一个选项

避免让应用程序受子进程支配的替代方法是什么?stdout

标签: pythoniosubprocessgreenlets

解决方案


推荐阅读