2013-12-16 1 views
3

Чтение нескольких вопросов по этой теме. Теперь я понимаю, что дочерний процесс наследует дескрипторы файла из родительского процесса. Это затруднит получение ребенком EOFError, когда родитель закрывает соединение.MultiProcessing Pipe recv блокирует, даже когда дочерний процесс не работает

Но моя ситуация наоборот, и я не понимаю, с чем сталкиваюсь.

У меня есть родительский процесс, который запускает дочерний процесс и дает ему доступ к одному из концов подключенного соединения. Теперь, когда дочерний процесс завершен, сбои или что-то еще, все остановлено и соединение закрыто. В этот момент дочерний процесс показывает, что он не функционирует.

Затем я ожидаю, что соединение родительского процесса вызовет EOFError на вызов блокировки recv. Но вместо этого он просто сидит и ждет.

Что мне здесь не хватает?

EDIT

Я думаю, что этот пример представляет собой проблему:

from multiprocessing import Process, Pipe 
from threading import Thread 
import time 

class Parent(object): 

    def __init__(self): 
     self.parent_conn, child_conn = Pipe() 
     self.child = Process(target=Child, args=(child_conn,)) 
     self.child.start()   

     def recv(): 
      try: 
       self.parent_conn.recv() 
      except EOFError: 
       print "EOF" 
      except: 
       print "something else" 

     # Does not work 
     recv() 

     # Works fine 
     t = Thread(target=recv) 
     t.setDaemon(True) 
     t.start() 

    def close(self): 
     self.parent_conn.close() 
     self.child.join() 

class Child(object): 

    def __init__(self, conn): 
     conn.close() 

if __name__ == "__main__": 
    p = Parent() 
    time.sleep(1) 
    p.close() 

Если я использую отдельный поток, родитель имеет право закрыть свое собственное подключение и все работает отлично. (Обратите внимание, что вам все равно нужно знать, как это сделать для этого ребенка). Вместо этого, если я вызову recv напрямую, он будет блокироваться, но я подозреваю, что он поднимет EOFError, как только дочерний процесс завершит соединение. Но это не так. Может ли кто-нибудь уточнить?

+0

Не могли бы вы предоставить минимальный пример кода, демонстрирующий проблему? – jfs

+0

@ J.F.Sebastian, я привел пример. Чтобы показать, что recv сохраняет блокировку. Я также предоставил второй вариант, который показывает, что он может легко закрыть, если это станет возможным. –

+1

добавить 'child_conn.close()' после 'self.child.start()'. [Идиоматично работать с трубами, чтобы закрыть неиспользуемые концы] (https://gist.github.com/zed/7540510). Также (необязательно) укажите параметр 'duplex = False'. Если '.close()' работает для вас; пожалуйста, [разместите его как свой собственный ответ] (http://stackoverflow.com/help/self-answer) для других. – jfs

ответ

4

Добавить child_conn.close() после self.child.start(). Это idiomatic for working with pipes to close unused ends. Также (необязательно) параметр duplex=False.

Дело в том, что я не знаю заранее, будет ли он сразу закрываться. Обычно ребенок должен иметь возможность отправлять и получать. Кроме того, я до сих пор не понимаю, почему это не сработает, как есть.

  1. child_conn.close() в родителя не означает, что ребенок должен закрыть свой конец сразу
  2. parent_conn.recv не возвращается до тех пор, пока есть шанс, что кто-то будет child_conn.send(). Если child_conn открыт (в ребенке или родителе), то есть это шанс

Если я использую отдельный поток, родитель имеет право закрыть свое собственное подключение и все работает отлично. Обратите внимание, что вам все равно нужно как-то узнать, что ребенок сделал для этого, для этого

Вам не обязательно знать это. Вы можете закрыть, как только соединение будет открыто у ребенка. Вызов child_conn.close() у родителя после self.child.start() прекрасен, что бы ни делал ребенок.

Не могли бы вы еще раз объяснить вариант дуплекса?

duplex=False означает, что труба является однонаправленным, т.е., можно назвать только parent_conn.recv() и child_conn.send(). В противном случае он двунаправлен, и оба соединения поддерживают send/recv.

+0

Да, это работает :). Мне потребовалось несколько минут, чтобы понять, что я, конечно, не должен закрывать связь до тех пор, пока я не передам ее ребенку ..: p Не могли бы вы еще раз объяснить вариант дуплекса? Я думаю, это означает, что только один конец может быть отправлен, если установлен false? –

+0

@Oxidator: Я добавил описание параметра 'duplex'. – jfs