Python在不阻塞父进程的情况下加入进程

Python在不阻塞父进程的情况下加入进程,python,multiprocessing,Python,Multiprocessing,我正在编写一个程序,它将监视特定目录中包含下载URL的新文件。一旦检测到一个新文件,它将创建一个新进程来进行实际下载,同时父级继续监视目录。我正在使用多处理中的进程界面。我遇到的问题是,除非调用process.join(),否则子进程仍在运行,但process.join()是一个阻塞函数,无法实现创建子进程来处理实际下载的目的 我的问题是,有没有一种方法可以以非阻塞的方式加入子进程,从而允许父进程继续做自己的事情 部分代码: def main(argv): # parse command l

我正在编写一个程序,它将监视特定目录中包含下载URL的新文件。一旦检测到一个新文件,它将创建一个新进程来进行实际下载,同时父级继续监视目录。我正在使用
多处理
中的
进程
界面。我遇到的问题是,除非调用process.join(),否则子进程仍在运行,但process.join()是一个阻塞函数,无法实现创建子进程来处理实际下载的目的

我的问题是,有没有一种方法可以以非阻塞的方式加入子进程,从而允许父进程继续做自己的事情

部分代码:

def main(argv):
  # parse command line args
  ...
  # set up variables
  ...
  watch_dir(watch_dir, download_dir)


def watch_dir(wDir, dDir):
  # Grab the current watch directory listing
  before = dict([(f, None) for f in os.listdir (wDir)])

  # Loop FOREVER
  while 1:
    # sleep for 10 secs
    time.sleep(10)

    # Grab the current dir listing
    after = dict([(f, None) for f in os.listdir (wDir)])

    # Get the list of new files
    added = [f for f in after if not f in before]
    # Get the list of deleted files
    removed = [f for f in before if not f in after]

    if added:
      # We have new files, do your stuff
      print "Added: ", ", ".join(added)

      # Call the new process for downloading
      p = Process(target=child, args=(added, wDir, dDir))
      p.start()
      p.join()

    if removed:
      # tell the user the file was deleted
      print "Removed: ", ", ".join(removed)

    # Set before to the current
    before = after

def child(filename, wDir, dDir):
  # Open filename and extract the url
  ...
  # Download the file and to the dDir directory
  ...
  # Delete filename from the watch directory
  ...
  # exit cleanly
  os._exit(0)
父级等待子级完成执行,然后在
p.join()之后继续执行(据我所知),这是正确的。但这违背了创造孩子的全部目的。如果我退出
p.join()
,则子对象将保持活动状态,并且
ps ax | grep
python将为我提供“python”


我想让孩子完成它所做的事情,然后离开,而不耽误父母。有办法吗?

您可以设置一个单独的线程来进行连接。让它在您将子进程句柄推入的进程上侦听:

class Joiner(Thread):
    def __init__(self, q):
        self.__q = q
    def run(self):
        while True:
            child = self.__q.get()
            if child == None:
                return
            child.join()

然后,代替
p.join()
,执行
joinq.put(p)
并执行
joinq.put(None)
以发出线程停止的信号。请确保使用FIFO队列。

如果您不关心子进程何时终止以及是否终止,并且您只想避免子进程以僵尸进程结束,那么您可以执行双分叉,以便孙辈最终成为
init
的子进程。代码:

def child(*args):
  p = Process(target=grandchild, args=args)
  p.start()
  os._exit(0)

def grandchild(filename, wDir, dDir):
  # Open filename and extract the url
  ...
  # Download the file and to the dDir directory
  ...
  # Delete filename from the watch directory
  ...
  # exit cleanly
  os._exit(0)

在while循环中,调用

multiprocessing.active_children()
返回当前进程的所有活动子进程的列表。
调用此函数的副作用是“加入”任何已完成的进程。

与其试图强迫
multiprocessing.Process()
为您工作,不如使用不同的工具,如multiprocessing.Pool():

multiprocessing.Pool()
是可以向其提交“作业”的辅助进程池。
pool.apply_async()
函数调用会导致其中一个子进程使用提供的参数异步运行函数,并且在脚本完成所有工作并关闭整个池之前不需要加入。库为您管理详细信息

我认为这比目前公认的答案更适合您,原因如下:
1.它消除了仅仅为了管理子流程而启动额外线程和队列的不必要复杂性。
2.它使用专门为此目的而设计的库例程,因此您可以从未来的库改进中获益。
3.依我看,它更易于维护。
4.这是一种更灵活的方法。如果有一天您决定实际查看子流程的返回值,您可以存储
apply\u async()
call(a)中的返回值,并随时检查它。您可以将它们存储在一个列表中,当列表超过一定大小时,将它们作为一个批处理。您可以将池的创建移动到
watch_dir()
函数中,并取消try/除非您并不真正关心如果“无限”循环被中断会发生什么。如果您在(当前)无限循环中放入某种中断条件,您只需在循环结束后添加
pool.close()
pool.join()
,所有内容都已清除。

您还可以与
deamon=True
(守护进程)一起使用;
process.start()
方法不会阻塞,因此父进程可以在不等待子进程完成的情况下继续工作

唯一需要注意的是,守护进程不允许产生子进程

from multiprocessing import Process

child_process = Process(
    target=my_func,
    daemon=True
)
child_process.start()
# Keep doing your stuff

我让它工作了,谢谢。我知道你们班做什么,我只是不确定我是否用对了。首先,我在
1:
循环之外创建一个队列(FIFO队列),然后在添加新文件时将
p
添加到该队列中。然后我调用
Joiner(q)
。虽然这是可行的,但我觉得我把它拼凑起来,让它发挥作用。有更好的方法吗?@Tarek,想法是:先创建一个队列,然后创建一个
加入者,然后循环。如果这样做,
Joiner
将与主循环同时运行,并在每个子进程完成后进行清理。(可能会有延迟,因为子进程的执行速度不同,但至少主线程不会阻塞。)啊,所以即使
q.get()
返回
Empty
,它仍然会失败
child==None
测试,因此不会返回,对吧?
q.get()
永远不会返回
Empty
。它会一直阻塞,直到它得到一个子进程加入,或者得到一个告诉它停止的
None
。@TarekFadel我已经陷入同一个问题好几天了。如果你能详细说明一下你到底是如何解决的,我会很有帮助。谢谢我仍然有一个
[python]
。您能帮忙吗?在这种情况下,加入的整个问题似乎都没有意义,因为文档中说,在Unix上加入僵尸进程,当进程完成但尚未加入时,它就变成了僵尸。不应该有太多,因为每次新进程启动(或调用active_children())时,所有尚未加入的已完成进程都将被加入。这感觉像是一种非常python的方式来解决主进程从未完成时的进程僵尸问题。我的意思是,这似乎不起作用,
pool.apply\u async
不调用该函数。它似乎只有在您调用其返回的
get
方法时才会运行,该方法会阻止主进程…@leoschet那么您的代码中还有其他错误,因为调用“apply”_
from multiprocessing import Process

child_process = Process(
    target=my_func,
    daemon=True
)
child_process.start()
# Keep doing your stuff