【问题标题】:Event Driven System Call in PythonPython中的事件驱动系统调用
【发布时间】:2015-07-17 05:24:48
【问题描述】:

我正在尝试使用系统调用或子流程来实现事件驱动的流程。基本上我想启动一个非阻塞系统命令,并在完成该系统调用后,我想要调用一个函数。 这样我就可以启动一个 GUI 进度条,启动一个系统命令并让进度条继续,当系统调用完成时,让进度条停止。

我绝对不想做的是生成一个进程,获取它的进程 ID 并在 while 循环中继续检查该进程的完成情况。

以下只是我想象的应该如何工作的示例(所有这些都在一个类中)

def launchTool(self):

    self.progressbar.config(mode = 'indeterminate')
    self.progressbar.start(20)
    self.launchButton.config(state = 'disabled')
    self.configCombobox.config(state = 'disabled')

    ##  here the "onCompletion" is a pointer to a function
    call("/usr/bin/make psf2_dcf", shell=True, onCompletion = self.toolCompleted)


def onCompletion(self):

    print('DONE running Tool')

    self.progressbar.stop()
    self.launchButton.config(state = 'normal')
    self.configCombobox.config(state = 'normal')

【问题讨论】:

  • 产生一个线程,使用回调
  • 您使用的是什么 GUI 框架?执行此操作的正确方法在某种程度上取决于此。
  • 我正在使用 tkinter 作为 GUI
  • @Mario,你能说得更具体点吗?我正在研究,事实证明我可以使用“进程”和“队列”生成一个进程。你是这个意思吗。像这样:q = Queue() p1 = Process(target=someFunction, args=(q,)) p1.start()
  • 你的操作系统是什么?你不需要线程;您可以处理 SIGCHLD 信号(写入 fd 以在信号处理程序中触发 I/O 事件或直接使用诸如 signalfd 之类的 OS 接口(使用 tk.createfilehandler 订阅以侦听 I/O 事件)。跨度>

标签: python progress-bar subprocess event-driven


【解决方案1】:

为避免轮询子进程的状态,您可以在 Unix 上使用SIGCHLD 信号。要将其与 tkinter 的事件循环结合使用,您可以使用 the self-pipe trick。它还可以解决 the possible tkinter + signal issue 的问题,而无需定期唤醒事件循环。

#!/usr/bin/env python3
import logging
import os
import signal
import subprocess
import tkinter

info = logging.getLogger(__name__).info

def on_signal(pipe, mask, count=[0]):
    try:
        signals = os.read(pipe, 512)
    except BlockingIOError:
        return # signals have been already dealt with

    # from asyncio/unix_events.py
    #+start
    # Because of signal coalescing, we must keep calling waitpid() as
    # long as we're able to reap a child.
    while True:
        try:
            pid, status = os.waitpid(-1, os.WNOHANG)
        except ChildProcessError:
            info('No more child processes exist.')
            return
        else:
            if pid == 0:
                info('A child process is still alive. signals=%r%s',
                     signals, ' SIGCHLD'*(any(signum == signal.SIGCHLD
                                              for signum in signals)))
                return
            #+end
            # you could call your callback here
            info('{pid} child exited with status {status}'.format(**vars()))
            count[0] += 1
            if count[0] == 2:
                root.destroy() # exit GUI


logging.basicConfig(format="%(asctime)-15s %(message)s", datefmt='%F %T',
                    level=logging.INFO)
root = tkinter.Tk()
root.withdraw() # hide GUI

r, w = os.pipe2(os.O_NONBLOCK | os.O_CLOEXEC) 
signal.set_wakeup_fd(w) 
root.createfilehandler(r, tkinter.READABLE, on_signal)
signal.signal(signal.SIGCHLD, lambda signum, frame: None) # enable SIGCHLD
signal.siginterrupt(signal.SIGCHLD, False) # restart interrupted syscalls automatically
info('run children')
p = subprocess.Popen('sleep 4', shell=True)
subprocess.Popen('sleep 1', shell=True)
root.after(2000, p.send_signal, signal.SIGSTOP) # show that SIGCHLD may be delivered
root.after(3000, p.send_signal, signal.SIGCONT) # while the child is still alive
root.after(5000, lambda: p.poll() is None and p.kill()) # kill it
root.mainloop()
info('done')

输出

2015-05-20 23:39:50 run children
2015-05-20 23:39:51 16991 child exited with status 0
2015-05-20 23:39:51 A child process is still alive. signals=b'\x11' SIGCHLD
2015-05-20 23:39:52 A child process is still alive. signals=b'\x11' SIGCHLD
2015-05-20 23:39:53 A child process is still alive. signals=b'\x11' SIGCHLD
2015-05-20 23:39:54 16989 child exited with status 0
2015-05-20 23:39:54 No more child processes exist.
2015-05-20 23:39:54 done

【讨论】:

    猜你喜欢
    • 2020-09-08
    • 1970-01-01
    • 2011-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多