Windows 上的异步子进程

2024-04-22

首先,我要解决的总体问题比我在这里展示的要复杂一些,所以请不要告诉我“使用阻塞线程”,因为如果没有公平、公平的重写,它就无法解决我的实际情况重构。

我有几个不需要我修改的应用程序,它们从标准输入获取数据,并在发挥其魔力后将其输出到标准输出。我的任务是链接其中几个程序。问题是,有时他们会窒息,因此我需要跟踪他们在 STDERR 上输出的进度。

pA = subprocess.Popen(CommandA,  shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# ... some more processes make up the chain, but that is irrelevant to the problem
pB = subprocess.Popen(CommandB, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=pA.stdout )

现在,直接通过 pA.stdout.readline() 和 pB.stdout.readline() 或普通的 read() 函数读取是一个阻塞问题。由于不同的应用程序以不同的速度和不同的格式输出,因此不能选择阻塞。 (正如我上面所写的,除非万不得已,否则线程不是一个选择。)pA.communicate()是死锁安全的,但由于我需要实时信息,所以这也不是一个选择。

因此谷歌把我带到了这个异步子流程片段 http://code.activestate.com/recipes/440554-module-to-allow-asynchronous-subprocess-use-on-win/在活动状态上。

一开始一切都很好,直到我实施它。比较 cmd.exe 的输出pA.exe | pB.exe,忽略两个输出到同一窗口造成混乱的事实,我看到非常即时的更新。但是,我使用上面的代码片段和read_some()函数在那里声明,并且需要 10 秒以上才能通知单个管道的更新。但当它发生时,它会进行更新,例如将进度一路提升至 40%。

因此,我做了一些更多的研究,并看到许多有关 PeekNamedPipe、匿名句柄以及即使管道中有可用信息也返回 0 字节的主题。由于事实证明这个主题远远超出了我的专业知识,无法修复或编码,因此我来到 Stack Overflow 寻求指导。 :)

我的平台是 W7 64 位和 Python 2.6,应用程序是 32 位(以防万一),并且与 Unix 的兼容性不是问题。我什至可以处理一个完整的 ctypes 或 pywin32 解决方案,如果它是唯一的解决方案,它可以完全颠覆子进程,只要我可以异步地从每个 stderr 管道中读取数据,并立即执行性能并且没有死锁。 :)


必须使用线程有多糟糕?我遇到了很多同样的问题,最终决定使用线程来收集子进程的 stdout 和 stderr 上的所有数据,并将其放入线程安全队列中,主线程可以以阻塞方式读取该队列,而不必担心幕后发生的线程。

目前尚不清楚基于线程和阻塞的解决方案预计会出现什么问题。您是否担心必须使其余代码成为线程安全的?这不应该成为问题,因为 IO 线程不需要与任何其余代码或数据交互。如果您的内存要求非常严格,或者您的管道特别长,那么您可能会对生成如此多的线程感到不高兴。我对你的情况不太了解,所以我不能说这是否可能是一个问题,但在我看来,既然你已经产生了额外的进程,那么与它们交互的一些线程不应该是一个可怕的负担。在我的情况下,我还没有发现这些 IO 线程特别有问题。

我的线程函数看起来像这样:

def simple_io_thread(pipe, queue, tag, stop_event):
    """
    Read line-by-line from pipe, writing (tag, line) to the
    queue. Also checks for a stop_event to give up before
    the end of the stream.
    """
    while True:
        line = pipe.readline()

        while True:
            try:
                # Post to the queue with a large timeout in case the
                # queue is full.
                queue.put((tag, line), block=True, timeout=60)
                break
            except Queue.Full:
                if stop_event.isSet():
                    break
                continue
        if stop_event.isSet() or line=="":
            break
    pipe.close()

当我启动子进程时,我这样做:

outputqueue = Queue.Queue(50)
stop_event = threading.Event()
process = subprocess.Popen(
    command,
    cwd=workingdir,
    env=env,
    shell=useshell,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE)
stderr_thread = threading.Thread(
    target=simple_io_thread,
    args=(process.stderr, outputqueue, "STDERR", stop_event)
)
stdout_thread = threading.Thread(
    target=simple_io_thread,
    args=(process.stdout, outputqueue, "STDOUT", stop_event)
)
stderr_thread.daemon = True
stdout_thread.daemon = True
stderr_thread.start()
stdout_thread.start()

然后,当我想读取时,我可以阻塞输出队列 - 从它读取的每个项目都包含一个字符串来标识它来自哪个管道,以及来自该管道的一行文本。很少有代码在单独的线程中运行,并且它仅通过线程安全队列与主线程通信(加上一个事件,以防我需要提前放弃)。也许这种方法很有用,可以让您通过线程和阻塞来解决问题,但不必重写大量代码?

(我的解决方案变得更加复杂,因为我有时希望尽早终止子进程,并希望确保线程全部完成。如果这不是问题,您可以摆脱所有 stop_event 内容,它会变得非常简洁。)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Windows 上的异步子进程 的相关文章

随机推荐

  • IIS 7 URL 重写

    我已经为此工作了几个小时 现在正试图让它发挥作用 我有一个 CodeIgniter 网站 曾经在 Apache 服务器上运行良好 我使用 Apache URL Rewriter 作为 URL 来隐藏 index php 由于某种原因 我无法
  • python 的日志记录模块在 cygwin 下报告不正确的时区

    我正在 Windows 7 上的 cygwin 下运行使用日志记录模块的 python 脚本 date命令报告正确时间 date Tue Aug 14 2012 2 47 49 PM 然而 Python 脚本已经关闭了五个小时 2012 0
  • SQL 查询中的“NOT LIKE”

    为什么这个简单的查询会返回 ORA 00936 缺少表达式 正如您所知 数据库是 Oracle SELECT FROM transactions WHERE id NOT LIKE 1 AND NOT LIKE 2 我觉得自己很傻 但我做错
  • 如何使用 MS SQL 2008 获取数据库中的表列表?

    我想验证数据库中是否存在表 如果不存在 则创建它 如何获取当前数据库中所有表的列表 我可以使用这样的 SELECT 获取数据库列表 SELECT FROM sys databases 剩下的就是创建该表 如果该表不存在 我还尝试与数据库同时
  • +exposeBinding 不起作用

    我试图在 NSWindowController 的子类中公开自定义绑定 我在子类中添加了以下代码 void initialize self exposeBinding customBinding 然后 在 IB 中 我有一个子类的对象实例
  • 如何暂停使用DownloadManager?

    我想实现下载时 可以由用户暂停 停止 如何使用 DownloadManager 实现此目的 You can 删除下载 http developer android com reference android app DownloadMana
  • 如何从外部网站重新创建图像预览?

    与 Facebook 的 UI 类似 我尝试从外部链接网站生成预览图像 这样 当用户输入要链接的 url 时 UI 将默认扫描该网站以查找图像并抓取预览缩略图 这项技术有具体的名称吗 或者有人可以指出我学习这个的方向吗 非常感谢 其名为刮
  • 如何调整外部 SWF 的大小以适合容器?

    我想要完成的是调整外部 SWF 的大小 使其适合在舞台上作为容器呈现的显示对象 现在它显示在容器外部 重要提示 我不希望外部 SWF 占据整个舞台 我在舞台上为它准备了一个特殊的地方 那个容器 public function loaderC
  • Perl CGI 脚本根据运行返回不同的结果

    我有一个 Perl CGI 脚本 它明显随机地发出不同的 HTML 所有输入都没有改变 例如 我会跑wget两次并得到两个不同的结果 CGI 由开发数据库支持 该数据库也不会改变 我有一个调试语句 通知我相同数量的元素从数据库返回到脚本中
  • 将 csv 数据写入命名空间内的矩阵时,TCL 抛出无效命令名称

    这是一个奇怪的问题 我似乎无法弄清楚 我正在使用 TCL 8 5 我正在尝试使用以下命令将数据从 CSV 文件读取到矩阵中csv read2matrix命令 然而 每次我这样做时 它都会说我试图写入的矩阵是无效命令 我正在做的事情的片段 p
  • 嵌入式 JavaScript 中的特殊字符

    我有一些嵌入在 html 文件中的 javascript 如下所示 它有一条像这样的线 if os Mac br Safari br Chrome 一切顺利 这意味着脚本可以工作 但是验证者 http validator w3 org 正在
  • 在 App Engine 标准 python 中使用 Google Stackdriver 日志时出错

    我的堆栈 谷歌应用程序引擎标准Python 2 7 Goal 要在 Google Stackdriver Logging 中创建命名日志 https console cloud google com logs viewer https co
  • 如何将这些数据存储在cookies中?

    假设我有一些文本框 文本区域 其中的值必须存储 这些值必须在按键时存储 以便当用户过早关闭页面时不会丢失数据 这是我当前的代码 使用cookie function createCookie name value days if days v
  • 箭头呢?

    阅读有关 Haskell 各种类别主题课程的各种教程 我们发现诸如Monoid Functor Monad等等 所有这些都有数十个实例 但由于某种原因 当我们到达Arrow 只有两个实例 函数和 monad 在这两种情况下 使用Arrow与
  • 如何使用 Go 的 flag 包打印位置参数的用法?

    鉴于这个简单的 Go 程序只需要一个命令行参数 我该如何改进它以便flag Usage 给出有用的输出 package main import flag fmt os func main flag Parse if len flag Arg
  • Python中不可变对象的类型是什么(对于mypy)

    我总是用mypy in my Python程式 类型是什么 来自typing 对于不可变对象 那些可以用作字典键的对象 回到上下文中 我想编写一个从字典继承的类 并且我有以下代码 class SliceableDict dict def g
  • Pthreads 与 OpenMP

    我正在使用 Linux 用 C 创建一个多线程应用程序 我不确定是否应该使用 POSIX 线程 API 还是 OpenMP API 使用两者有何优缺点 Edit 有人可以澄清这两个 API 是否创建内核级 or 用户级线程 Pthreads
  • 获取特定时区的当前时间

    我有一个具有不同时区的日期和时间格式的数据框 我想将其与该时区的当前时间进行比较 所以我想在下面的 日期和时间 列中添加 1 小时 然后将其与该时区的当前时间进行比较 就像第一个一样 时区是 EDT 当前时间是 2017 07 18 10
  • Java 枚举和 Switch 语句 - 默认情况?

    对于建议抛出异常的人 抛出异常不会给我带来编译时错误 它会给我带来运行时错误 我知道我可以抛出异常 我宁愿在编译期间死也不愿在运行时死 首先 我使用的是 eclipse 3 4 我有一个数据模型 其模式属性是枚举 enum Mode on
  • Windows 上的异步子进程

    首先 我要解决的总体问题比我在这里展示的要复杂一些 所以请不要告诉我 使用阻塞线程 因为如果没有公平 公平的重写 它就无法解决我的实际情况重构 我有几个不需要我修改的应用程序 它们从标准输入获取数据 并在发挥其魔力后将其输出到标准输出 我的