在循环内重复使用相同的管道时出现“Broken Pipe”错误

时间:2016-11-17 23:05:55

标签: python pipe multiprocessing fork ipc

我是进程间通信的新手,我试图在Python中了解os.pipeos.fork之间的用法。

在下面的代码中,如果我取消注释行“Broken Pipe”错误,否则它正常工作。

想法是在子进程退出时使用SIGCHLD处理程序,并在仅子函数(run_child)和仅父函数(sigchld_handler)执行时递增相应的计数器。由于分叉进程将拥有自己的内存版本,并且更改不会反映在父进程中,因此尝试让子进程通过管道向父进程发送消息并让父进程更新计数器。

import os
import signal
import time

class A(object):
    def __init__(self):
        self.parent  = 0
        self.child = 0
        self._child_pid = None

        self.rd , self.wr = os.pipe()
        print self.rd , self.wr
        signal.signal(signal.SIGCHLD, self.sigchld_handler)

    def sigchld_handler(self, a, b):
        self.parent += 1
        print "Main run count : (parent) ", self.parent
        #rf = os.fdopen(self.rd, 'r')
        #self.child = int(rf.read())
        #rf.close()
        self._child_pid = None

    def run_child(self):
        self.child += 1
        print "Main run count (child) : ", self.child
        print "Running in child : " , os.getpid()
        wr = os.fdopen(self.wr,'w')
        text = "%s" % (self.child)
        print "C==>", text
        wr.write(text)
        wr.close()
        os._exit(os.EX_OK)

    def run(self):
        if self._child_pid:
            print "Child Process", self._child_pid, " already running."
        else:
            self._child_pid = os.fork()

            if not self._child_pid:
                self.run_child()

a = A()
i = 0
while True:
    a.run()
    time.sleep(4)
    i += 1
    if i > 5:
        break

有趣的是,在前几次迭代后出现错误。有人可以解释为什么错误即将来临,我该怎么做才能解决这个问题。

编辑1: 有几个类似的示例:ex1ex2ex3。我实际上只使用它们来学习,但在我的情况下,我正在扩展示例以在循环中运行以更像生产者/消费者队列。我理解这可能不是一个好方法,因为Python中提供了多进程/队列模块,但我想了解我在这里犯的错误。

编辑2(解决方案):

基于@S.kozlov's answer,修改代码以为每次通信创建新管道。这是修改后的代码。

import os
import pdb
import signal
import time

class A(object):
    def __init__(self):
        self.parent  = 0
        self.child = 0
        self._child_pid = None
        signal.signal(signal.SIGCHLD, self.sigchld_handler)

    def sigchld_handler(self, a, b):
        self.parent += 1
        os.close(self.wr)
        print "Main run count : (parent) ", self.parent
        rd = os.fdopen(self.rd, 'r')
        self.child = int(rd.read())
        self._child_pid = None

    def run_child(self):
        self.child += 1
        print "Main run count (child) : ", self.child
        print "Running in child : " , os.getpid()
        os.close(self.rd)
        wr = os.fdopen(self.wr, 'w')
        text = "%s" % (self.child)
        print "C==>", text
        wr.write(text)
        wr.close()
        os._exit(os.EX_OK)

    def run(self):
        if self._child_pid:
            print "Child Process", self._child_pid, " already running."
        else:
            self.rd , self.wr = os.pipe()
            self._child_pid = os.fork()

            if not self._child_pid:
                self.run_child()

a = A()
i = 0
while True:
    a.run()
    time.sleep(4)
    i += 1
    if i > 5:
        break

有了这个,输出应该来(像这样)。

Main run count (child) :  1
Running in child :  15752
C==> 1
Main run count : (parent)  1
Main run count (child) :  2
Running in child :  15753
C==> 2
Main run count : (parent)  2
Main run count (child) :  3
Running in child :  15754
C==> 3
Main run count : (parent)  3
Main run count (child) :  4
Running in child :  15755
C==> 4
Main run count : (parent)  4
Main run count (child) :  5
Running in child :  15756
C==> 5
Main run count : (parent)  5
Main run count (child) :  6
Running in child :  15757
C==> 6
Main run count : (parent)  6 

1 个答案:

答案 0 :(得分:2)

您的代码的问题在于您尝试多次重复使用一个管道,并且它通常不是管道的有效情况。 你刚才说的例外情况是:“嘿,你在前一次运行中关闭了这个管道。一旦管道关闭,它就关闭了。”。

因此,您可以更改代码以为每个子项创建管道,将一端(读取)存储在“父”中,并将另一端存储给子项。然后它应该工作。

编辑1.我已经用关于“每个孩子的一个管道”的东西更新了你的代码,这不是好的代码应该如何,但在教育意义上希望它会有所帮助。

import os
import signal
import time


class A(object):
    def __init__(self):
        self.parent = 0
        self.child = 0
        self._child_pid = None
        signal.signal(signal.SIGCHLD, self.sigchld_handler)

    def sigchld_handler(self, a, b):
        self.parent += 1
        print "Main run count : (parent) ", self.parent
        os.close(self.wr)
        rf = os.fdopen(self.rd, 'r')
        message = rf.read()
        rf.close()
        print "Code from child [", self._child_pid, "]: ", message
        self.rd = None
        self._child_pid = None

    def run_child(self):
        self.child += 1
        print "Main run count (child) : ", self.child
        print "Running in child : " , os.getpid()
        os.close(self.rd)
        wr = os.fdopen(self.wr, 'w')
        text = "Hello from %s" % (self.child)
        print "C==>", text
        wr.write(text)
        wr.close()
        os._exit(os.EX_OK)

    def run(self):
        if self._child_pid:
            print "Child Process", self._child_pid, " already running."
        else:
            rd, wr = os.pipe()
            self.rd = rd
            self.wr = wr
            self._child_pid = os.fork()

            if not self._child_pid:
                self.run_child()
a = A()
i = 0
while True:
    a.run()
    time.sleep(4)
    i += 1
    if i > 5:
        break