Python Process对象join方法详解:并发与串行的区别

何川
何川
发布时间:2026-10-13 | 5人浏览过
原创

本文深入解析Python multiprocessing模块中Process对象的join方法。通过对比主进程独立运行与依赖子进程结果的两种场景,阐明join用于阻塞主进程直至子进程结束的核心机制。重点澄清常见误区:多次调用join不会导致子进程串行执行,总耗时取决于最长子进程。通过代码示例展示并发与串行调用的时间差异,帮助开发者正确使用join进行资源回收和流程控制。

Process对象的join方法详解

Process对象的join方法详解

主进程与子进程的任务关系

在主进程运行过程中,若需并发执行其他任务,通常通过开启子进程实现。此时,主进程与子进程的任务关系主要分为两种情况。

情况一:

当主进程任务与子进程任务彼此独立时,主进程任务先执行完毕。此时,主进程仍需等待子进程执行完毕,以便统一回收资源。这种场景下无需使用join方法。

情况二:

若主进程在执行到某一阶段时,必须等待子进程执行完毕才能继续后续操作,

则需要一种机制让主进程检测子进程的运行状态。只有当子进程执行完毕后,主进程才继续执行;否则主进程将在原地阻塞。这就是join方法的主要作用。

该机制确保主进程等待所有子进程执行完毕后,主进程才继续执行后续逻辑。


from multiprocessing import Process
import time
import os
def task():
  print("%s is running,parent id is <%s>" % (os.getpid(), os.getppid()))
  time.sleep(3)
  print("%s is done,parent id is <%s>" % (os.getpid(), os.getppid()))
if __name__ == "__main__":
  t = Process(target=task, )
  t.start()
  t.join()
  # 主进程 等子进程执行完了
  print("主", os.getpid(), os.getppid())
'''
is running,parent id is <25956>
is done,parent id is <25956>
主 25956 2992
'''

当子进程运行结束后,最后打印主进程信息,主进程随之结束,所有僵尸进程资源得到回收。

开启多个子进程并向操作系统发送信号时,由于操作系统处理任务繁重,先开启哪个子进程是随机的,甚至有时可能先开启主进程。

操作系统何时开启子进程、开启多长时间,开发者通常无法确切知晓。


from multiprocessing import Process
import time
import os
def task(name):
  print('%s is running' %name)
  time.sleep(2)
  print('%s is end' %name)
if __name__ == '__main__':
  p1 = Process(target=task, args=('子进程1',))
  p2 = Process(target=task, args=('子进程2',))
  p3 = Process(target=task, args=('子进程3',))
  p4 = Process(target=task, args=('子进程4',))
  p1.start()
  p2.start()
  p3.start()
  p4.start()
  print('主',os.getpid(),os.getppid())
'''
子进程1 is running
子进程2 is running
主 9268 5236
子进程3 is running
子进程4 is running
子进程1 is end
子进程2 is end
子进程3 is end
子进程4 is end

'''

也可能出现先开启主进程的情况,


主 9556 5236
子进程1 is running
子进程3 is running
子进程2 is running
子进程4 is running
子进程1 is end
子进程3 is end
子进程2 is end
子进程4 is end

p.start() 仅仅是向操作系统发送启动信号。

join会让进程变成串行执行吗?

join会让进程变成串行执行吗?

既然join用于等待进程结束,如果按如下方式编写代码,进程是否会变成串行执行? 当然不会。必须明确:p.join()是让谁等待? 显然,p.join()是让主线程等待p子进程的结束,卡住的是主进程,而非子进程p本身。


from multiprocessing import Process
import time
import os
def task(name):
  print('%s is running' %(name))
  time.sleep(2)
  print('%s is end' %(name))
if __name__ == '__main__':
  p1 = Process(target=task, args=('子进程1',))
  p2 = Process(target=task, args=('子进程2',))
  p3 = Process(target=task, args=('子进程3',))
  p4 = Process(target=task, args=('子进程4',))
  p1.start()
  p2.start()
  p3.start()
  p4.start()
  p1.join()
  p2.join()
  p3.join()
  p4.join()
  print('主',os.getpid(),os.getppid())

详细解析如下:

进程只要调用start就会开始运行,因此p1-p4.start()执行时,系统中已经存在四个并发运行的进程。

而p1.join()是在等待p1结束。没错,只要p1不结束,主线程就会一直卡在原地,这也是问题的关键所在。

join是让主线程等待,而p1-p4仍然是并发执行的。当执行p1.join时,其余p2、p3、p4仍在运行。等到p1.join结束,可能p2、p3、p4早已结束,此时p2.join、p3.join、p4.join只需通过检测即可通过,无需额外等待。

因此,4个join花费的总时间仍然等于耗时最长的那个进程运行的时间。

所以,这并非串行执行,而是并发执行。

4个join花费的总时间仍然是耗费时间最长的那个进程运行的时间。

因此总耗时为5秒,即子进程1所需的等待时间。


from multiprocessing import Process
import time
import os
def task(name,n):
  print('%s is running' %(name))
  time.sleep(n)
  print('%s is end' %(name))
if __name__ == '__main__':
  start = time.time()
  p1 = Process(target=task, args=('子进程1',5))
  p2 = Process(target=task, args=('子进程2',2))
  p3 = Process(target=task, args=('子进程3',2))
  p4 = Process(target=task, args=('子进程4',2))
  p1.start()
  p2.start()
  p3.start()
  p4.start()
  p1.join()
  p2.join()
  p3.join()
  p4.join()
  print('主',time.time() - start)
'''
子进程1 is running
子进程2 is running
子进程3 is running
子进程4 is running
子进程2 is end
子进程3 is end
子进程4 is end
子进程1 is end
主 5.413309812545776
'''

这种方式即为串行执行。

在等待子进程1执行时,子进程2尚未发送信号;需等待子进程1执行完毕后,再发送子进程2的信号,开启子进程2并执行,按照这样的顺序进行。


from multiprocessing import Process
import time
import os
def task(name,n):
  print('%s is running' %(name))
  time.sleep(n)
  print('%s is end' %(name))
if __name__ == '__main__':
  start = time.time()
  p1 = Process(target=task, args=('子进程1',5))
  p2 = Process(target=task, args=('子进程2',2))
  p3 = Process(target=task, args=('子进程3',2))
  p4 = Process(target=task, args=('子进程4',2))
  p1.start()
  p1.join()
  p2.start()
  p2.join()
  p3.start()
  p3.join()  
  p4.start()
  p4.join()
  print('主',time.time() - start)
'''
子进程1 is running
子进程1 is end
子进程2 is running
子进程2 is end
子进程3 is running
子进程3 is end
子进程4 is running
子进程4 is end
主 12.212698698043823

'''

进程启动与join的简写形式

上述启动进程与join进程的操作可以简写为以下形式。


from multiprocessing import Process
import time
import os
def task(name,n):
  print('%s is running' %(name))
  time.sleep(n)
  print('%s is end' %(name))
if __name__ == '__main__':
  start = time.time()
  p1 = Process(target=task, args=('子进程1',5))
  p2 = Process(target=task, args=('子进程2',2))
  p3 = Process(target=task, args=('子进程3',2))
  p4 = Process(target=task, args=('子进程4',2))
  process_list = [p1,p2,p3,p4]
  for p in process_list:
    p.start()
  for p in process_list:
    p.join()
  print('主',time.time() - start)

join保证所有子进程执行完毕后,主进程才能继续工作,否则主进程将一直阻塞。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表