V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
kayseen
V2EX  ›  Python

如何终止某一线程中的 subprocess.Popen 以及非阻塞读取多个终端输出

  •  
  •   kayseen · 2022-04-27 09:55:26 +08:00 · 2380 次点击
    这是一个创建于 952 天前的主题,其中的信息可能已经有所发展或是发生改变。

    需求是使用 subprocess 执行一个耗时命令,对终端的输出进行非阻塞的实时监控,当终端输出 error 信息时,关闭 Popen ,或者执行一段时间后自动关闭 Popen 。因为同时需要实时来监控多个终端输出,需要非阻塞。我依次使用了以下两种方法:

    1 、 最初的实现为使用 asyncio 创建一个 loop 并且 run_forever() ,然后将读取 Popen 输出的阻塞方法( While Popen is None )注册到该 loop 上运行,读取 stdout 输出,如果没有检测到 error 信息,就 asyncio.sleep(0.01);后来我发现当存在多个 Popen 的时候,sleep 切换会导致有的终端输出读取丢失,并不能实时读取到,所以我采用第二种方法;

    2 、 第二次实现是给每个 Popen 创建一个线程,使用线程的 daemon start 来实时监控,资源消耗会比 1 大但是目前还没有遇到读取实时监控的问题。

    然后又有一个需求,如果用户想要中途终止该进行监控的 subprocess 的 Popen ,该如何中途停止该 Popen 呢?

    另求:各路大神有没有比上述方案 1 和方案 2 中更适合的实现非阻塞实时读取输出的方案?

    8 条回复    2022-04-27 13:56:13 +08:00
    ysc3839
        1
    ysc3839  
       2022-04-27 10:37:45 +08:00
    我不了解 Python 。但是异步框架一般有读 fd 的功能吧?同时读所有管道对应的 fd 就好了。
    julyclyde
        2
    julyclyde  
       2022-04-27 11:01:24 +08:00
    不能实时还是不能读到?
    LeeReamond
        3
    LeeReamond  
       2022-04-27 11:10:26 +08:00
    看了一下以前自己的实现,subprocess.Popen 是同步代码,肯定是不在事件循环的线程里执行的,创建子进程后用 process.stdout.fileno()拿到 stdout ,然后用 selectors.DefualtSelector 挂上,印象里这个 default 在 linux 下是会自动用 epoll ,推流用 call_soon_threadsafe 就推回事件循环了。关闭子进程用的是三方库 psutil ,selector 挂起无法自动关闭,解决方法是加个 timeout ,最慢 10 秒内关闭。
    kayseen
        4
    kayseen  
    OP
       2022-04-27 11:53:49 +08:00
    @ysc3839 我曾经试过通过管道的 fd 来读取,但是也会因为 while Popen is None 阻塞线程,也有可能是我的使用方式不对
    kayseen
        5
    kayseen  
    OP
       2022-04-27 11:58:49 +08:00
    @julyclyde 感谢回复。这是针对使用 asyncio 的 loop 实现的问题,我曾经复现过,是因为开启多个 popen 的时候,通过 asyncio.sleep 来切换读取其他的 popen ,如果一个 popen 此时输出了 error ,然后自动关闭了 popen ,因为 loop 此时切换到了其他的 popen 读取,就会丢失从 error 到关闭的范围内的输出
    kayseen
        6
    kayseen  
    OP
       2022-04-27 12:01:54 +08:00
    @LeeReamond 感谢回复,思考了好一会,感觉思路挺清晰,大多是没有接触过的东西,要好好消化一会儿 qaq 。。。
    wwqgtxx
        7
    wwqgtxx  
       2022-04-27 13:36:10 +08:00   ❤️ 2
    asyncio 本来不就有对 popen 的支持么
    https://docs.python.org/3/library/asyncio-subprocess.html
    https://docs.python.org/3/library/asyncio-eventloop.html#running-subprocesses
    多开几个 task 去用你的第二次实现的思路不就得了
    lolizeppelin
        8
    lolizeppelin  
       2022-04-27 13:56:13 +08:00
    linux 下用 fork exec 加 select 管道去折腾

    折腾熟了 subprocess 什么回事自然就知道了

    协程的话 eventlet 里有 GreenPile ,asyncio 里应该也有类似封装
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3567 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 11:02 · PVG 19:02 · LAX 03:02 · JFK 06:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.