【问题标题】:How to execute a bunch of shell commands serially without blocking the main thread?如何在不阻塞主线程的情况下串行执行一堆shell命令?
【发布时间】:2021-07-04 16:19:00
【问题描述】:

我的问题是我有一个需要从 python 程序 UI 串行执行的 shell 命令列表。 shell 命令可能需要 10 秒到 10 分钟才能完成。我希望它们在后台运行而不阻塞主 UI/线程,所以我继续使用 python 程序。我尝试了以下方法来运行许多 shell 命令。

command_list = list()
for i in command_list:
    os.system("Running command: ", i)

os.system()会一一执行命令列表,但是会阻塞主线程 同样适用于subprocess.run()subprocess.call()

subprocess.Popen() 不会阻塞主线程,而是会并行运行所有的 shell 命令,这是不可取的。

过去几天我尝试在 python discord 中搜索和询问,但无法找到解决问题的方法。

编辑: 假设我有这个 GUI 脚本,只是一个带有两个 QPushButton(“开始”、“停止”)的 QMainWindow。 “开始”将运行一个 shell 命令列表。 "Stop" 将停止 shell 命令的执行。

class Window(qt.QMainWindow):
    def __init__(self):
    super().__init__()
    central_widget = qt.QWidget()
    central_widget.setLayout(qt.QHBoxLayout())
    self.setCentralWidget(central_widget)

    centra_widget.layout().addWidget(qt.QPushButton("Start", clicked = self.run_command))
    centra_widget.layout().addWidget(qt.QPushButton("Stop", clicked = self.stop_command))

    def run_command(self):
        for cmd in cmd_list:
            subprocess.Popen("Run command", cmd)

    def stop_command(self):
        # Do something to stop the shell commands

app = qt.QApplication([])
main_window = Window()
main_window.show()
app.exec_()

上面的代码会同时运行列表中的所有命令。

所以我尝试将 sleep() 放在 Popen 之间,但它也会冻结主 UI,并且无法单击“停止”按钮。

这是我最初的问题,即如何按顺序运行 shell 命令而不冻结主 UI/线程。谢谢你的时间。

【问题讨论】:

标签: python-3.x subprocess


【解决方案1】:

subprocess.Popen 只启动一个进程。你的调用程序需要不时地poll它来确定它是否已经完成,然后才运行下一个命令。

import subprocess
import shlex
import time

# ...

for cmd in command_list:
    print('running command:', cmd)
    p = subprocess.Popen(shlex.split(cmd))
    result = None
    while result is None:
        time.sleep(1) # or whatever else you want to do here
        result = p.poll()
    if result != 0:
        raise CalledProcessError(
            '%s failed, result code %i', cmd, result)

如果你的程序是由 GUI 主循环控制的,你可能想把它翻过来,就像这样:让主循环运行一个回调,比如说,每秒,然后在回调中做轮询和前一个进程完成后可能会产生一个新进程。

如果您出于其他原因运行线程,则可以循环遍历命令列表并在专用线程中使用 subprocess.check_call() 或类似名称运行每个命令。

【讨论】:

  • 我想你可以通过multiprocessing.Pool(processes=1) 运行所有这些,它会创建一个包含单个进程的池。然后multiprocessing 库将负责等待一个进程完成和下一个进程开始。
  • 感谢您的提示。你能给我看一个代码示例吗?
  • subprocess documentation 有很多简单的例子;你基本上想Popen 然后定期poll Popen 对象。您的问题没有包含有关调用代码的足够信息,无法让我们猜测 Popenpoll 之间应该发生什么,所以这必然是模糊的。
  • 感谢您的宝贵时间。我更新了代码以提供有关我的用例的更具体信息。希望它清除它。
  • 感谢您的更新。我不熟悉你的 GUI 框架,但快速鸭鸭让我stackoverflow.com/questions/51828943/pyqt5-and-subprocess-popen
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-19
  • 2011-01-19
  • 1970-01-01
  • 1970-01-01
  • 2021-04-13
  • 2020-08-16
  • 1970-01-01
相关资源
最近更新 更多