Python threading中主进程与子线程的三种交互模式解析

季宁
季宁
发布时间:2026-10-13 | 2人浏览过
原创

本文详解Python threading模块中主进程与子线程的三种交互方式:使用join()使主线程等待子线程结束;通过setDaemon(True)设置守护线程,主线程退出时子线程随之终止;以及未调用任何方法时的默认挂起行为。文章包含具体代码示例与性能对比,帮助开发者掌握线程同步技巧。

Python多线程中主进程与子线程的关系处理

在使用Python进行多线程编程时,理清主进程与子线程的生命周期关系至关重要。早期常因对join和setDaemon函数理解不足导致逻辑混乱,现通过总结这两种机制来明确其作用。

使用join()方法等待子线程结束

调用join()方法后,主线程会阻塞在调用点,直到对应的子线程执行完毕,主线程才会继续向下执行后续代码。

以下是join()函数的具体应用示例:


import time
import random
import threading
 
class worker(threading.Thread):
  def __init__(self): 
    threading.Thread.__init__(self) 
  def run(self):
    t = random.randint(1,10)
    time.sleep(t)
    print "This is " + self.getName() + ";I sleep %d second."%(t)
    
tsk = []
for i in xrange(0,5):
  time.sleep(0.1)
  thread = worker()
  thread.start()
  tsk.append(thread)
for tt in tsk:
  tt.join()
print "This is the end of main thread."

程序运行后的输出结果如下所示:


# python testjoin.py 
This is Thread-3;I sleep 2 second.
This is Thread-1;I sleep 4 second.
This is Thread-2;I sleep 7 second.
This is Thread-4;I sleep 7 second.
This is Thread-5;I sleep 7 second.
This is the end of main thread.

该示例创建了5个子线程,每个线程随机休眠1至10秒后打印退出信息;主线程依次调用join()等待这5个子线程全部结束。最终输出顺序为先显示各子线程的日志,最后才打印主线程的结束信息。

使用setDaemon()设置守护线程

若使用setDaemon()方法,其行为与join()相反:主线程结束时不会等待子线程,子线程将随主线程的退出而被强制终止。

setDaemon()函数的具体使用示例如下:


import time
import random
import threading
 
class worker(threading.Thread):
  def __init__(self): 
    threading.Thread.__init__(self) 
  def run(self):
    t = random.randint(1,10)
    time.sleep(t)
    print "This is " + self.getName() + ";I sleep %d second."%(t)
    
tsk = []
for i in xrange(0,5):
  time.sleep(0.1)
  thread = worker()
  thread.setDaemon(True)
  thread.start()
  tsk.append(thread)
print "This is the end of main thread."

在此示例中,子线程被设置为守护线程(Daemon)。当主线程执行完毕并结束时,所有子线程会被立即中止,不再等待其完成。

运行上述代码后的结果如下:

#python testsetDaemon.py This is the end of main thread.

默认情况下的线程行为

若既不使用join()也不使用setDaemon(),主线程在创建并启动子线程后,会立即继续执行后续代码。此时主线程处于挂起状态,直到所有子线程执行完毕,主进程才会真正结束。


import time
import random
import threading
 
class worker(threading.Thread):
  def __init__(self): 
    threading.Thread.__init__(self) 
  def run(self):
    t = random.randint(1,10)
    time.sleep(t)
    print "This is " + self.getName() + ";I sleep %d second."%(t)
    
tsk = []
for i in xrange(0,5):
  time.sleep(0.1)
  thread = worker()
  thread.start()
  tsk.append(thread)
print "This is the end of main thread."

未使用join或setDaemon时的运行结果如下:


# python testthread.py 
This is the end of main thread.
This is Thread-4;I sleep 1 second.
This is Thread-3;I sleep 7 second.
This is Thread-5;I sleep 7 second.
This is Thread-1;I sleep 10 second.
This is Thread-2;I sleep 10 second.

Thread与Process的性能对比分析

补充知识:Python中Thread(线程)与Process(进程)的性能对比

造成这种差异的原因在于进程和线程的资源调度方向不同,此处仅针对该特定实例进行分析。


# coding=utf-8
import logging
import multiprocessing
import os
import time
from threading import Thread

logging.basicConfig(
  level=logging.INFO,
  format="%(asctime)s 【 %(process)d 】 %(processName)s %(message)s"
)


def func (i):
  # logging.info(f'子:{os.getpid()},\t{i}')
  return f'子:{os.getpid()},\t{i}'


def main (ctx):
  start01 = time.time()
  ts = [Thread(target=func, args=(i,)) for i in range(100)]
  [t.start() for t in ts]
  [t.join() for t in ts]
  end01 = time.time() - start01
  logging.info(f"线程花费的时间:{end01}秒")
  
  start02 = time.time()
  ps = [ctx.Process(target=func, args=(i,)) for i in range(100)]
  [p.start() for p in ps]
  [p.join() for p in ps]
  end02 = time.time() - start02
  logging.info(f"进程花费的时间:{end02}秒")


if __name__ == '__main__':
  # windows 启动方式
  multiprocessing.set_start_method('spawn')
  # 获取上下文
  ctx = multiprocessing.get_context('spawn')
  # 检查这是否是冻结的可执行文件中的伪分支进程。
  ctx.freeze_support()
  main(ctx)

程序输出的日志信息如下:

2019-10-06 14:17:22,729 【 7412 】 MainProcess 线程花费的时间:0.012967586517333984秒 2019-10-06 14:17:25,671 【 7412 】 MainProcess 进程花费的时间:2.9418249130249023秒

以上关于Python threading中主进程与子线程关系的处理方法是本文分享的全部内容,希望能为读者提供参考,并欢迎大家支持本站。

本站声明

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

Python 相关产品和应用

全部下载

专题列表