【问题标题】:Broken pipe during a subprocess stdin.write子进程 stdin.write 期间管道损坏
【发布时间】:2013-04-18 15:24:27
【问题描述】:

我与用于标记句子的服务器交互。此服务器在端口2020 上本地启动。

例如,如果我通过下面使用的客户端在端口2020 上发送Je mange des pâtes .,服务器回答Je_CL mange_V des_P pâtes_N ._.,结果总是只有一行,如果我的输入不为空,则总是一行。

我目前必须通过此服务器标记 9 568 个文件。前 9 483 个文件按预期标记。之后,输入流似乎已关闭/已满/其他,因为当我尝试在 stdin 上写入时收到 IOError,特别是 Broken Pipe 错误。

当我跳过前 9 483 个第一个文件时,最后一个文件被标记为没有任何问题,包括导致第一个错误的文件。

我的服务器没有产生任何错误日志,表明发生了一些可疑的事情......我是否处理错误?一段时间后管道出现故障是否正常?

log = codecs.open('stanford-tagger.log', 'w', 'utf-8')
p1 = Popen(["java",
            "-cp", JAR,
            "edu.stanford.nlp.tagger.maxent.MaxentTaggerServer",
            "-client",
            "-port", "2020"],
           stdin=PIPE,
           stdout=PIPE,
           stderr=log)

fhi = codecs.open(SUMMARY, 'r', 'utf-8') # a descriptor of the files to tag

for i, line in enumerate(fhi, 1):
    if i % 500:
        print "Tagged " + str(i) + " documents..."
    tokens = ... # a list of words, can be quite long
    try:
        p1.stdin.write(' '.join(tokens).encode('utf-8') + '\n')
    except IOError:
        print 'bouh, I failed ;(('
    result = p1.stdout.readline()
    # Here I do something with result...
fhi.close()

【问题讨论】:

  • 好吧,代码看起来有点危险——你假设结果总是一行。也可能是在远程主机上运行的服务器对您可以在单个 TCP 连接中发送的数据总量实施了一些最大限制。也可能是 Java 客户端的问题。
  • @Aya:我进行了编辑以反映我在本地运行服务器的事实(我可以发送的数据总量是否仍然存在限制?我真的不知道这些事情) .另外,答案总是一行,我认为问题不来自那里。即使在调试模式下,Java 也不会产生任何错误,这让我感到困惑 :( 感谢您的输入!
  • 这在很大程度上取决于客户端和服务器代码,以及客户端如何将其标准输入/标准输出转换为 TCP 数据包/从 TCP 数据包转换。如果您在本地运行服务器,您可以尝试从等式中消除客户端,并让 Python 直接连接到服务器。
  • 我不想直接操作套接字,但你是对的,它有助于调试。感谢您的提示:)

标签: python subprocess pipe


【解决方案1】:

除了我的 cmets,我可能会建议其他一些更改...

for i, line in enumerate(fhi, 1):
    if i % 500:
        print "Tagged " + str(i) + " documents..."
    tokens = ... # a list of words, can be quite long
    try:
        s = ' '.join(tokens).encode('utf-8') + '\n'
        assert s.find('\n') == len(s) - 1       # Make sure there's only one CR in s
        p1.stdin.write(s)
        p1.stdin.flush()                        # Block until we're sure it's been sent
    except IOError:
        print 'bouh, I failed ;(('
    result = p1.stdout.readline()
    assert result                               # Make sure we got something back
    assert result.find('\n') == len(result) - 1 # Make sure there's only one CR in result
    # Here I do something with result...
fhi.close()

...但是考虑到还有一个我们一无所知的客户端/服务器,有很多地方可能会出错。

如果您将所有查询转储到一个文件中,然后使用类似...的命令行运行它,它是否有效?

java .... < input > output

【讨论】:

  • 我会用你提出的新元素进行调查。当我发现新东西时我会报告:)
  • @Mog 还注意到Python docs 中的警告:Use communicate() rather than .stdin.write, .stdout.read or .stderr.read to avoid deadlocks due to any of the other OS pipe buffers filling up and blocking the child process. 尽管这需要为每个输入行重新调用Popen。可以说,即使是基于管道的 IPC 也可能非常棘手。 :)
  • 实际上,我正在经历这整个过程,以避免在每一步都调用 Popen(加载一个 jvm,x 9 000,这很痛苦)。但是是的,这似乎正是我遇到的那种问题:-(