multiprocessing的子类。进程永不加入

问题描述 投票:3回答:1

我制作了一个继承了multiprocessing.Process的类:

class Task(Process):
    def run(self) :
        print("RUN")

        # doing some works

        self.close()
        print("closed")

我以这种方式启动它:

proc = Task()
proc.start()
proc.join()
print("Joined")

但是它不会加入,输出将是这样的:

>> RUN
>> closed

我没有使用任何类型的QueuesPipes。当我在Ubuntu上运行它时,我用pid跟踪了该过程。即使在完成self.close()行之后,过程仍然存在,没有任何例外。我也在Windows上运行了此程序,并在任务管理器中跟踪了进程。 self.close()之后进程退出,但仍然没有加入。另一点是在Ubuntu上,在self.close()之后一切都卡住了,当我按Ctrl + C时,我得到了:

Traceback (most recent call last):
  File "Scheduler.py", line 164, in <module>
    scheduler.start()
  File "Scheduler.py", line 152, in start
    self.enqueueTask(plan)
  File "Scheduler.py", line 134, in enqueueTask
    proc.join()
  File "/usr/local/lib/python3.8/multiprocessing/process.py", line 149, in join
    res = self._popen.wait(timeout)
  File "/usr/local/lib/python3.8/multiprocessing/popen_fork.py", line 47, in wait
    return self.poll(os.WNOHANG if timeout == 0.0 else 0)
  File "/usr/local/lib/python3.8/multiprocessing/popen_fork.py", line 27, in poll
    pid, sts = os.waitpid(self.pid, flag)

根据最后一行,我想主要过程正在等待某些东西,但是为什么?为什么?

更新1

由于@Darkonaut的注释,我调试了整个程序。问题出在我从non-daemonrun()方法启动的Task线程中。因此,我可以肯定地说,即使MainThread完成后,线程仍会阻止我的进程关闭。但我仍然感到困惑,因为该non-daemon线程的目标函数已成功完成。所以我更具体的问题是为什么非守护进程线程应该保持并防止杀死其进程,即使在30秒后完成其目标之后?

python multithreading multiprocessing python-multiprocessing python-multithreading
1个回答
0
投票

为什么非守护程序线程应保留并阻止其进程被执行被杀死,即使目标在30秒后完成?

场景

没有看到子进程中新线程的实际作用,观察到的行为的可能情况是您的thread-1正在开始[[另一个 thread-2,您甚至可能不知道。 multiprocessing.Queue.put()可能是从您正在调用的第三方库开始的,或者是留在stdlib中的,Process还会在后台启动供稿器线程。

此一般方案既不是Process.close()子类问题,也不涉及从子进程本身内部调用MainThread(用法不正确,但没有后果)。

进程中的_shutdown()始终是进程中退出的最后一个线程,并且它作为MainThread例程的一部分加入

non-daemonic线程。这就是thread-1处于“表面”工作状态时将其保持为边缘状态的原因。

问题出在我在Task的run()方法中启动的非守护进程线程。因此,我可以肯定地说,即使在MainThread完成后,该线程仍会阻止我的进程关闭。但是我仍然感到困惑,因为该非守护进程线程的目标函数已成功完成。

现在在此所示方案中,thread-1

can

的目标函数实际上已成功完成。但是,此thread-2已启动另一个thread-1,然后该操作会持续很长时间,例如在最坏的情况下会永远阻塞。
问:如果thread-1本身不是问题,为什么将daemon设为"initial value is inherited from the creating thread"时也没有挂起?

这是因为守护程序标志的thread-1。因此,除非显式设置了daemonthread-2标志,否则将daemon设为daemon也会使其“子级” thread-2成为Process。守护程序在关闭时未加入,并且整个过程“在没有活动的非守护程序线程离开时退出”。


代码

下面的示例有意不使用子类thread-2来表明这种情况已经可以通过更简单的设置重现。Timer这是一个threading.Barrier(parties=1).wait()线程,它将在10秒后启动并调用.wait()。然后,此parties=1呼叫将立即以parties=2结束,或者永远以.wait()阻塞,因为在我们的设置中不存在对此Barrier进行呼叫的其他方。这样可以轻松切换我们要复制的行为。

Barrier

下面的日志用于import threading
from multiprocessing import Process   


def t1_target(parties):  # # Process-1/Thread-1
    """Start another thread and exit without joining."""
    logger = get_mp_logger()
    logger.info(f"ALIVE: {[t.name for t in threading.enumerate()]}")
    timer = threading.Timer(10, threading.Barrier(parties=parties).wait)  # thread-2
    timer.name = "TimerThread"
    timer.start()
    logger.info(f"ALIVE: {[t.name for t in threading.enumerate()]}")
    logger.info("t1_target() done.")


def p_target(parties, daemon_thread=False):  # Process-1/MainThread
    """Start and join a thread, then exit."""
    logger = get_mp_logger()
    logger.info(f"ALIVE: {[t.name for t in threading.enumerate()]}")
    t = threading.Thread(target=t1_target, args=(parties,), daemon=daemon_thread)
    t.start()
    t.join()
    logger.info(f"ALIVE: {[t.name for t in threading.enumerate()]}")
    logger.info("p_target() done.")    


if __name__ == '__main__':

    import logging

    logger = get_mp_logger(logging.INFO)    
    p = Process(target=p_target, kwargs={'parties': 1, 'daemon_thread': False})
    p.start()
    p.join()
    logger.info("p.join() done.")
,因此没有无限阻塞,但是由于parties=1不是守护线程,因此thread-2将在关机时加入它。

注意MainThread完成后,TimerThread仍然有效。这里最主要的兴趣是t1_target中的MainThread如何需要大约10秒才能从Process-1变为"process shutting down"。这是"process exiting with exitcode 0"还活着的10秒。

使用TimerThread,它将在此阶段永久挂起,除非您还为parties=2(继承daemon)或直接为thread-1设置了thread-2标志。

thread-2

[INFO/Process-1] child process calling self.run()
[14:38:18,204 Process-1/MainThread] ALIVE: ['MainThread']
[14:38:18,204 Process-1/Thread-1] ALIVE: ['MainThread', 'Thread-1']
[14:38:18,204 Process-1/Thread-1] ALIVE: ['MainThread', 'Thread-1', 'TimerThread']
[14:38:18,204 Process-1/Thread-1] t1_target() done.
[14:38:18,204 Process-1/MainThread] ALIVE: ['MainThread', 'TimerThread']
[14:38:18,204 Process-1/MainThread] p_target() done.
[14:38:18,204 Process-1/MainThread] process shutting down
[14:38:28,204 Process-1/MainThread] process exiting with exitcode 0

Helper

[14:38:28,212 MainProcess/MainThread] p.join() done. [14:38:28,213 MainProcess/MainThread] process shutting down Process finished with exit code 0
© www.soinside.com 2019 - 2024. All rights reserved.