如何终止某一线程中的 subprocess.Popen 以及非阻塞读取多个终端输出 - V2EX
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 2795 次点击
    这是一个创建于 1294 天前的主题,其中的信息可能已经有所发展或是发生改变。

    需求是使用 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     Solana     5272 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 08:36 PVG 16:36 LAX 00:36 JFK 03:36
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86