如果子进程结束则终止应用程序

Terminate application if subprocess ends

我有一个应用程序正在其主线程中进行一些数据处理。到目前为止,它是一个纯控制台应用程序。现在我不得不添加一个用于可视化目的的 QT 应用程序,并将其作为一个单独的线程来完成。

如果QTWindow关闭了,主线程当然还有运行s。 window 关闭后如何终止主线程?

class Window(threading.Thread)
  def __init__(self, data_getter):
      super(Window, self).__init__()
      self.getter = data_getter


  def update(self):
      data = self.getter()
      #update all UI widgets

  def run(self):

      app: QApplication = QApplication([])

      app.setStyleSheet(style.load_stylesheet())
      window = QWidget()
      window.setWindowTitle("Test Widget")
      window.setGeometry(100, 100, 600, 300)

      layout = QGridLayout()
      self.LABEL_state: QLabel = QLabel("SM State: N/A")
      layout.addWidget(self.LABEL_state)
      window.setLayout(layout)
      window.show()

      timer = QTimer()
      timer.timeout.connect(self.update)
      timer.start(1000)

      app.exec_()
        
class Runner:
  def __init__(self)
    pass
      
  def data_container(self):
    return data
    
  def process_data(self):
    #do the data processing

def main():
    runner: Runner = Runner()

    time.sleep(1)

    w = Window(runner.data_container)
    w.start()

    while True:
        runner.process_data()
        time.sleep(2)

      
if __name__ == "__main__": main()

我最好的想法是给 Window Runner 的另一个函数引用,然后在 Window 中注册到 atexit 并设置一个经常在内部检查的终止标志主进程(Runner)。有更好的方法吗?我知道将 QApp 运行 作为主要进程可能会更好,但在这种情况下我不想这样做。

这里基本上有两个问题:跨两个线程同步事件,以及从外部停止 运行ning 线程。你解决后一个问题的方式可能会影响你解决前一个问题的方法。非常广泛地,您可以:

  • 在主循环中轮询一些标志(在您的情况下,main 中的 while True 循环将是一个明显的目标,可能会将逻辑移至 process_data 并使其 运行完成),或
  • 使用某种机制停止包含进程(如信号),可选择注册清理代码以使事情进入已知状态。

无论哪种情况,您都可以随心所欲地设计 api,但是 .stop().cancel() 方法是非常正常的解决方案。

依赖轮询的问题在于,最坏情况下的响应时间是主循环的整个周期。如果这不可接受,您可能想要触发包含进程或寻找更频繁地检查的方法(如果您的 process_data() 需要 << 2s 到 运行,请将 sleep(2) 替换为循环更小的延迟并在那里轮询标志)。

如果通过设置标志停止不可行,您可以触发包含进程。这通常意味着触发代码是 运行ning 在不同的 thread/process 中。 Python 的线程没有 .terminate(),但 multiprocessing.Processdo,因此您可以将处理委托给一个进程,然后让主代码调用 .terminate()(或者自己获取pid,手动发送信号)。在这种情况下,主代码在发出信号之前什么都不做,或者可能什么都不做。

最后,图形线程和处理线程之间的通信取决于您如何实现其余部分。对于简单地设置一个标志,暴露一个方法就可以了。如果将处理代码移动到 Process 并且主线程空闲,请使用阻塞事件来避免 busy-looping.

是的,如果图形线程是主线程并自行启动和停止处理代码,那会更容易。除非您知道这会使事情变得非常复杂,否则请查看它以了解您需要进行多少更改才能做到这一点:设计良好的数据处理代码应该只是获取数据、处理数据并将其推出。如果将它放入一个线程中是一项艰巨的工作,那么该设计可能需要重新审视。最后还有 'nuclear option' 只是在 window 循环中获取主线程的 pid 并终止它。这太骇人听闻了,但对于演示工作来说可能已经足够好了。