gpt4 book ai didi

python - "subprocess.Popen().readline()"在多线程python中无法返回

转载 作者:行者123 更新时间:2023-12-01 09:16:40 29 4
gpt4 key购买 nike

下面的代码在Win7中运行,有线程T1和T2,T1在原窗口中打印dir内容,T2在新窗口中ping 4秒。

import os
import sys
import logging
import subprocess
import threading

class T1 (threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
proc = subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE)
for line in iter(proc.stdout.readline, ''):
logging.debug(line)
logging.info("HEREEEEEEEE")

class T2 (threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
subprocess.Popen(["ping.exe", "-n", "4", "127.0.0.1"], creationflags=subprocess.CREATE_NEW_CONSOLE)
logging.info("")

if __name__=='__main__':
logger = logging.getLogger('root')
FORMAT = "[TID:%(thread)d %(funcName)s L#%(lineno)s] %(message)s"
logging.basicConfig(format=FORMAT, level=logging.DEBUG)

t1 = T1()
t2 = T2()
t1.start()
t2.start()
t1.join()
t2.join()

sys.exit(0)

对于 T1 线程中的 logging.info("HEREEEEEEEE") 行,我认为应该在打印 dir 的内容后立即打印它。

对我来说没有意义的是:为什么该行没有立即打印,而是在线程 T2 完成后 4 秒后打印?

不知道是否与多线程中的文件描述符有关。

最佳答案

对于 Python 2,您的代码存在竞争条件,可能会泄漏 T1 创建的管道的可继承写入端。线程进入由 T2 创建的 ping.exe 进程线。 readline在管道关闭之前,管道上的 不会返回,这需要关闭写入端的所有句柄。

在这种情况下,您可以通过传递close_fds=True来避免竞争条件。至Popen创建 ping.exe 进程时。这可以防止它继承可继承的句柄,包括 T1 中重叠调用的管道句柄。线程。

一般来说,Python 3.7之前,如果需要支持并发Popen使用覆盖的标准句柄进行调用,那么您需要包装 Popen使用通过预先获取锁来同步调用的函数。不幸的是,这使得 Popen调用多线程进程中的瓶颈,但没有简单的替代方案。

<小时/>

背景

在 Unix 中,close_fds Popen的参数适用于 fork 之后的子进程。如果为 true,则在调用 exec 之前将关闭所有非标准文件描述符。 ,即使没有FD_CLOEXEC标志设置。但是,它不会关闭标准文件描述符——stdin (0)、stdout (1) 和 stderr (2)。

在 Windows 中,句柄可以标记为可继承(即 HANDLE_FLAG_INHERIT )。默认情况下,句柄是不可继承的。如果CreateProcess被调用 bInheritHandles如果为 true,则所有可继承的句柄都由子级继承。 Popen将其传递为 not close_fds ,即不关闭文件描述符意味着继承句柄[*]。

在 Windows 中,stdin , stdout ,和stderr Popen的参数用于在 STARTUPINFO 中显式设置子级的标准句柄(即 hStdInputhStdOutputhStdError )。如果未显式覆盖标准句柄,则父级的标准句柄将由控制台应用程序(例如 python.exe)隐式继承,但 GUI 应用程序(例如 pythonw.exe)不会隐式继承。如果显式设置,则句柄必须可继承,并且 bInheritHandles (即 not close_fds )必须为真。当另一个线程重叠调用 CreateProcess 时,这就是竞争条件的根源。它还继承了句柄。

在 Python 3 中,通过默认 close_fds 降低了这种竞争条件的频率。当标准句柄未被覆盖时为 true。在 3.7 中,通过在 lpAttributeList 中传递标准句柄进一步缓解了这一问题。 STARTUPINFOEX 领域。通过此更改,并发调用 Popen可以覆盖标准句柄而不泄漏句柄。但是,句柄仍然必须是可继承的,因此仍然存在对继承句柄的其他函数的并发调用的竞争条件,例如 os.systemos.spawnl .

<小时/>

[*] 请注意,尽管有参数名称,C“文件描述符”实际上并未在 Windows 中继承。而 C 运行时可以继承 system 的文件描述符。和 spawn/exec函数,其使用STARTUPINFO实现这一点没有记录。因此Popen只继承句柄。继承非标准句柄时,您需要将句柄值传递给子级(例如通过 stdin、命令行或环境变量),您可以通过 msvcrt.get_osfhandle 获取该句柄值。 。子进程可以通过msvcrt.open_osfhandle为继承的句柄打开一个新的文件描述符。 .

关于python - "subprocess.Popen().readline()"在多线程python中无法返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51190271/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com