Python threading中主进程与子线程的三种交互模式解析
本文详解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中主进程与子线程关系的处理方法是本文分享的全部内容,希望能为读者提供参考,并欢迎大家支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









