2010-05-23 66 views
3

我一直在研究需要管理外部流程的gui应用程序。与外部进程一起工作会导致很多问题,导致程序员的生活困难。我觉得这个应用程序的维护时间长得令人无法接受。我一直在试图列举使外部流程工作困难的事情,以便我可以想出减轻痛苦的方法。这种转变成我认为我会在这里发布的咆哮,以获得一些反馈意见,并为任何想要进入这些非常黑暗的水域的人提供一些指导。以下是我目前为止所得到的结果:处理外部流程

  1. 孩子的输出可能会与父母的输出混淆。这可能会导致两种输出误导并难以阅读。可能很难说出来自哪里。当事物异步时,弄清楚发生了什么变得更加困难。这里是一个人为的例子:

    import textwrap, os, time 
    from subprocess import Popen 
    test_path = 'test_file.py' 
    
    with open(test_path, 'w') as file: 
        file.write(textwrap.dedent(''' 
         import time 
         for i in range(3): 
          print 'Hello %i' % i 
          time.sleep(1)''')) 
    
    proc = Popen('python -B "%s"' % test_path) 
    
    for i in range(3): 
        print 'Hello %i' % i 
        time.sleep(1) 
    
    os.remove(test_path) 
    

    输出:

    Hello 0 
    Hello 0 
    Hello 1 
    Hello 1 
    Hello 2 
    Hello 2 
    

    我想我可以有子进程的输出写入到一个文件。但是每次我想看到一个打印语句的结果时,必须打开一个文件可能会很麻烦。

    如果我有子进程的代码,我可以添加一个标签,如print 'child: Hello %i',但是对于每次打印都可能会这样做。它增加了一些噪音输出。当然,如果我无法访问代码,我无法做到这一点。

    我可以手动管理过程输出。但是,然后你用线程和轮询以及类似的东西来打开一大堆蠕虫。

    一个简单的解决方案是处理像同步函数这样的进程,也就是说,在进程完成之前不再执行代码。换句话说,使过程阻塞。但是,如果你正在构建一个gui应用程序,那不起作用。这给我带来了下一个问题...

  2. 阻塞过程导致GUI无响应。

    import textwrap, sys, os 
    from subprocess import Popen 
    
    from PyQt4.QtGui import * 
    from PyQt4.QtCore import * 
    
    test_path = 'test_file.py' 
    with open(test_path, 'w') as file: 
        file.write(textwrap.dedent(''' 
         import time 
         for i in range(3): 
          print 'Hello %i' % i 
          time.sleep(1)''')) 
    
    app = QApplication(sys.argv) 
    button = QPushButton('Launch process') 
    def launch_proc(): 
        # Can't move the window until process completes 
        proc = Popen('python -B "%s"' % test_path) 
        proc.communicate() 
    button.connect(button, SIGNAL('clicked()'), launch_proc) 
    button.show() 
    app.exec_() 
    os.remove(test_path) 
    

    Qt提供其自身的过程称为包装其QProcess可以在这方面帮助。您可以将函数连接到信号以相对容易地捕获输出。这是我目前使用的。但是我发现所有这些信号都像goto声明一样可疑,并且可能导致意大利面代码。我想我想通过让QProcess的'finished'信号调用一个包含进程调用之后的所有代码的函数来获得排序阻塞行为。我认为这应该工作,但我仍然有点模糊的细节...

  3. 当您从子进程返回到父进程时,堆栈跟踪会中断。如果一个正常的函数拧紧,你会得到一个很好的完整的堆栈跟踪文件名和行号。如果一个子进程崩溃了,如果你有任何输出的话,你会很幸运。每当出现问题时,你最终不得不做更多的侦探工作。

  4. 说起来,输出在处理外部过程时有一种消失的方式。就像如果你通过windows'cmd'命令运行某些东西,控制台会弹出,执行代码,然后在你有机会看到输出之前消失。你必须通过/ k标志来使它坚持下去。类似的问题似乎总是出现。

    我想这两个问题3和4都有相同的根本原因:没有异常处理。异常处理旨在用于功能,它不适用于流程。也许有一些方法可以为流程的异常处理提供一些东西?我想这就是stderr的用途?但处理两个不同的流本身可能很烦人。也许我应该看看这更多...

  5. 进程可以挂起并坚持在背景中没有你意识到它。所以你最终会对你的电脑大喊大叫,因为它会变得很慢,直到你最终调出任务管理器,并在后台看到30个相同进程的实例。

    此外,挂起的后台进程可以以各种有趣的方式干扰进程的其他实例,例如通过持有文件句柄或类似的东西来引起权限错误。

    这似乎是一个简单的解决方案,如果子进程没有关闭它自己,父进程会在退出时终止子进程。但是,如果父级进程崩溃,清理代码可能不会被调用,并且子级可能被挂起。另外,如果父母等待孩子完成,并且孩子处于无限循环或某种情况,那么最终可能会有两个挂起的过程。

    此问题可能会导致问题2的额外乐趣,导致您的GUI完全停止响应并强制您使用任务管理器杀死所有内容。

  6. ,F ***以外ING报价

    参数往往需要传递给进程。这本身就令人头疼。特别是如果你正在处理文件路径。说...'C:/我的文档/无论/'。如果您没有引号,则字符串通常会在空格处被拆分并被解释为两个参数。如果你需要嵌套引号,你可以使用'和',但如果你需要使用两层以上的引用,你必须做一些令人讨厌的转义,例如:“cmd/k'python \'path 1 \'\路径2 \“””。

    很好的解决了这个问题传递参数的列表,而不是作为一个字符串。子过程可以让你做到这一点。

  7. 不能轻易地从一个返回数据子进程

    当然你也可以使用stdout但是如果你想为了调试目的而在这里打印一个print呢如果它希望输出的格式是某种特定的方式,并返回另一个前夕一切正常。

  8. 模糊的命令行标志和糟糕的基于终端的帮助系统。

    这些是我在使用os级应用程序时经常遇到的问题。就像我提到的/ k标志一样,为了让一个cmd窗口打开,谁的想法是?在这方面,Unix应用程序往往不太友善。希望你可以使用谷歌或StackOverflow找到你需要的答案。但是,如果没有,你会有很多无聊的阅读和令人费解的尝试和错误。

  9. 外部因素。

    这是一种模糊的。但是当你离开你自己脚本的相对庇护港来处理外部过程时,你会发现自己不得不在更大程度上处理“外部世界”。这是一个可怕的地方。各种各样的事情可能会出错。只是给一个随机的例子:一个进程运行的cwd可以修改它的行为。

可能还有其他问题,但那些是我迄今写下来的问题。还有其他的问题想要补充吗?有没有处理这些问题的建议?

回答

2

检出子流程模块。它应该有助于输出分离。我没有看到单独的输出流或单个流中的某种输出标签。

悬挂过程问题也很困难。我能够做出的唯一解决方案就是在外部过程中设置一个计时器,如果它没有按照规定的时间返回,就杀了它。粗糙,讨厌,如果其他人有一个很好的解决方案,我很乐意听到它,所以我也可以使用它。

你可以做的一件事来帮助处理完全无法管理的关机问题,就是保留一个pid文件的目录。每当你启动一个外部进程时,将一个文件写入你的pid文件目录,其名称是该进程的pid。当您知道该进程已完全退出时,请清除该pid文件。您可以使用pid目录中的内容来帮助清理崩溃或重新启动。

这可能不会提供任何满意的或有用的答案,但也许这是一个开始。

+0

一些不错的想法,谢谢。我想我会把这个视为已被接受的观察,因为没有人会回应。我有点惊讶这个问题没有得到更多的关注。哦,我想这只是表明没有任何简单的解决方案。 – 2010-05-24 22:44:09