关于subprocess.Popen进程阻塞

这篇博客讲述了作者在Android系统上使用subprocess.Popen()进行进程操作时遇到的阻塞问题。尝试了多种解决方案,如将输出写入文件、查询子进程状态等,最终通过添加三行代码成功解决问题,并探讨了subprocess.Popen.poll()的作用,了解了进程结束的状态码,以防止未来再次遇到类似问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于subprocess.Popen进程阻塞

背景

在Android系统上实现软件的安装和运行,因为涉及到对返回值的判断,所以经过多方对比后选择了subprocess.Popen()。百度找的常规写法,然后代码运行到一半就阻塞了。实际这行代码是执行了,但是不知道什么原因,资源没有得到及时的释放,然后就阻塞了。

这是阻塞的代码:
在这里插入图片描述

一路踩坑

百度后,选择了将输出写入文件中,结束后删除文件的方法,将subprocess.Popen()做以下改进:

    def adb_cmd(self,cmd):
        '''操作adb shell的方法'''
        #得到一个临时文件,在close后删除,
        out_temp = tempfile.TemporaryFile()
        fileno = out_temp.fileno()

        subprocess.Popen(cmd,stdout=fileno,stderr=fileno)
        time.sleep(10)
        out_temp.seek(0)   #指针移到开始处
        self.rt = out_temp.readlines()    
        out_temp.flush()
        out_temp.close()     #关闭文件,文件就会自动删除

结果还是阻塞,百度过各种方法,因为使用场景不同,都失败。

终于解决

最后还是请教大神。只添加了三行代码,困扰了我多天的问题就解决了。
改进后代码

 def adb_cmd(self,cmd):
        '''操作adb shell的方法'''
        #得到一个临时文件,在close后删除,
        out_temp = tempfile.TemporaryFile()
        fileno = out_temp.fileno()

        res = subprocess.Popen(cmd,stdout=fileno,stderr=fileno)
        time.sleep(10)
        if res.poll() == None:
            res.kill()
        res.wait(timeout=30)
        out_temp.seek(0)   #指针移到开始处
        self.rt = out_temp.readlines()    #拂去文件
        out_temp.flush()
        out_temp.close()     #关闭文件,文件就会自动删除

刨根问底

虽然问题解决了,但是还是明白原理是什么。避免下次踩坑
百度了一下subprocess.Popen.poll(),用于检查子进程是否已经结束

返回码:
0:正常结束
1:sleep
2:子进程不存在
5:kill
None:在运行

豁然开朗

将每个进程的返回码打印后:
在这里插入图片描述
果然是在启动探针时,进程没有正常结束。
终于知道这三行代码的意义

<think>我们正在讨论Pythonsubprocess模块中的两个主要函数:subprocess.run()和subprocess.Popen()。根据引用[1]和引用[2],我们可以总结如下: 1. **subprocess.run()**: - 这是Python 3.5及更高版本中引入的高级函数。 - 它会阻塞当前进程直到子进程执行完成,然后返回一个`CompletedProcess`对象。 - 使用简单,适合执行简单的命令,不需要与子进程进行交互或精细控制。 - 支持超时(timeout)参数,可以设置子进程的运行时间限制。 - 可以方便地捕获输出(stdout和stderr)和错误。 2. **subprocess.Popen()**: - 这是subprocess模块中更底层的类,提供了更多的灵活性和控制。 - 可以异步执行子进程,不会阻塞进程。 - 允许与子进程进行交互(例如,通过管道发送输入和读取输出)。 - 提供了更多的方法(如poll(), wait(), communicate(), terminate(), kill()等)来控制子进程。 - 需要手动管理输入/输出流和子进程的生命周期,因此使用起来更复杂。 **主要区别总结**: | 特性 | `subprocess.run()` | `subprocess.Popen()` | |------|---------------------|----------------------| | **执行方式** | 同步(阻塞直到完成) | 异步(非阻塞) | | **返回值** | `CompletedProcess`对象(包含returncode, stdout, stderr等) | `Popen`对象(需要调用方法获取状态和输出) | | **交互性** | 不支持交互(只能一次性输入输出) | 支持交互(可以逐行读写) | | **复杂度** | 简单易用 | 复杂,需要手动管理 | | **适用场景** | 简单的命令执行,不需要交互 | 需要交互、后台运行、精细控制(如终止、获取PID) | **示例代码对比**: 1. 使用`subprocess.run()`: ```python import subprocess result = subprocess.run(['ls', '-l'], capture_output=True, text=True) print(result.stdout) # 直接获取标准输出 print(result.returncode) # 返回码 ``` 2. 使用`subprocess.Popen()`: ```python import subprocess proc = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE, text=True) # 非阻塞,可以继续做其他事情 # 等待完成并获取输出 stdout, stderr = proc.communicate() print(stdout) print(proc.returncode) # 注意:在communicate之后returncode才有值 ``` **如何选择**: - 如果只需要运行一个命令并等待结果,使用`subprocess.run()`更简单。 - 如果需要后台运行、交互式输入输出、或者需要同时管理多个子进程,则使用`subprocess.Popen()`[^1][^2]。 §§相关问题§§ 1.Python中如何实时读取子进程的输出? 2. 使用`subprocess.Popen()`时,如何避免死锁? 3. 如何终止一个由`subprocess.Popen()`启动的子进程? 4. `subprocess.run()`的超时参数如何使用?</think>### `subprocess.run()` 与 `subprocess.Popen()` 的主要区别 #### 1. **执行模式与阻塞行为** - **`subprocess.run()`** 同步阻塞执行,父进程会**等待子进程结束**才继续执行后续代码[^1]。 示例: ```python result = subprocess.run(['ls', '-l'], capture_output=True) print("子进程已结束") # 在子进程结束后执行 ``` - **`subprocess.Popen()`** 异步非阻塞执行,父进程**立即继续执行**,不等待子进程结束[^1][^2]。 示例: ```python proc = subprocess.Popen(['ping', 'example.com']) print("立即执行") # 子进程仍在后台运行 ``` #### 2. **返回对象与控制能力** | 特性 | `subprocess.run()` | `subprocess.Popen()` | |---------------------|-----------------------------------------------|-----------------------------------------------| | **返回类型** | `CompletedProcess`对象 | `Popen`对象 | | **获取返回值** | 直接通过`result.returncode` | 需手动调用`proc.poll()`或`proc.wait()`[^2] | | **进程控制方法** | 无(自动结束) | 支持`terminate()`, `kill()`, `send_signal()` | | **实时交互能力** | ❌ 不支持 | ✅ 支持通过管道交互(`stdin.write()`) | #### 3. **输出处理方式** - **`subprocess.run()`** 通过参数直接捕获输出: ```python result = subprocess.run(['ls'], capture_output=True, text=True) print(result.stdout) # 直接获取完整输出 ``` - **`subprocess.Popen()`** 需手动管理管道和缓冲区[^2]: ```python proc = subprocess.Popen(['tail', '-f'], stdout=subprocess.PIPE) while True: line = proc.stdout.readline() # 实时逐行读取 if not line: break print(line.decode().strip()) ``` #### 4. **适用场景对比** | 场景 | 推荐方法 | 原因 | |--------------------------|-------------------|----------------------------------------------------------------------| | 简单命令,需等待结果 | `subprocess.run()`| 简洁安全,自动管理资源[^1] | | 后台运行任务 | `subprocess.Popen`| 非阻塞特性适合后台任务 | | 实时交互式进程(如CLI) | `subprocess.Popen`| 支持通过管道实时读写[^1] | | 需要精细控制(如终止进程)| `subprocess.Popen`| 提供进程控制方法(`kill()`, `terminate()`)[^2] | | 大输出量处理 | `subprocess.Popen`| 可流式处理输出,避免内存溢出 | #### 5. **典型代码模式** ```python # run() 模式:单次执行+获取结果 result = subprocess.run( ['grep', 'error', 'log.txt'], capture_output=True, timeout=10 # 自动超时控制 ) # Popen() 模式:交互式进程 proc = subprocess.Popen( ['python', '-i'], stdin=subprocess.PIPE, stdout=subprocess.PIPE ) proc.stdin.write(b"print(2+2)\n") proc.stdin.flush() print(proc.stdout.readline()) # 输出: b'4\n' proc.terminate() # 手动终止 ``` > 总结:优先使用 `subprocess.run()` 处理简单同步任务;需要交互、后台运行或精细控制时选择 `subprocess.Popen()`[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值