要理解守护线程(不分语言),这里有三个问题需要弄清楚:
笔者表达能力欠佳,下面还是让代码(以python代码为例)说话,看看守护线程到底特殊在哪里:
# 首先定义两个线程:听音乐和看电影 存在 threads数组中
def music(song_name):
for i in range(3):
print "I was listening to song: %s %s" % (song_name, i)
time.sleep(2)
def movie(movie_name):
for i in range(3):
print "I was watching movie:%s %s" % (movie_name, i)
time.sleep(2)
threads = []
t1 = threading.Thread(target=music, args=("蜉蝣",))
threads.append(t1)
t2 = threading.Thread(target=movie, args=("龙猫",))
threads.append(t2)
接下来运行一个普通多线程的例子:
if __name__ == "__main__":
print "主线程开始运行"
for t in threads:
t.start()
print "主线程运行结束"
下面是运行结果:
可以看到主线程运行结束了,子线程还在运行中,但是程序还是坚持等到所有子线程运行完了才退出
下面是一个守护线程的例子:
if __name__ == "__main__":
print "主线程开始运行"
for t in threads:
t.setDaemon(True) # 设置为守护线程 setDaemon必须在start方法之前完成
t.start()
print "主线程运行结束"
运行结果:
主线程运行结束但是守护线程没结束的情况下,整个进程直接结束了,守护线程随之终止
关于python多线程中join()方法的用途,网上有介绍说是用于等待守护线程运行结束,避免因主线程的提前结束而导致守护线程突然终止的问题。通过阅读join()方法的源码,我们发现不管是注释还是代码都没有任何关于主线程或者守护线程的字眼,只是说挂起其他线程,等待被调用线程运行结束后再唤醒其他线程的运行。说明上述解释略显片面,只能说join()方法多被用于该场景而已。
同样来看一段代码:
多线程的代码与(一)中的一致,我们只修改一下main方法中的代码
if __name__ == "__main__":
print "主线程开始运行"
t_start = time.time()
for t in threads:
t.setDaemon(True) # 设置为守护线程 setDaemon必须在start方法之前完成
t.start()
for t in threads:
t.join() # wait until the thread terminates
print "主线程运行结束"
t_end = time.time()
print "用时:"+str(t_start-t_end)
即设置守护线程并启动所有子线程后调用join(),以下是运行结果:
可以看到,主线程运行结束这句话的打印一直是出现在最后一行的,即join()方法使主线程挂起,等待守护线程运行完毕后被唤醒继续执行。join()方法完美地控制程序按照我们的期望顺序运行了。即使我们将上述代码中的 t.setDaemon()注释起来,运行结果也并没有什么改变,此种情形下,其实多线程是否一定要是守护线程就没必要强求了。
需要注意的是,子线程之间调用join()方法的顺序,当我们改写main方法为如下所示时(每个子线程start后立刻调用join(),而不是等待所有线程都启动后调用),就会是另一种状态了:
if __name__ == "__main__":
print "主线程开始运行"
ti = time.time()
for t in threads:
t.setDaemon(True) # 设置为守护线程 setDaemon必须在start方法之前完成
t.start()
# for t in threads:
t.join() # wait until the thread terminates
print "主线程运行结束"
t = time.time()
print "用时:"+str(t-ti)
运行结果如下:
结果很明显第一个子线程阻塞了第二个子线程的运行,第二个子线程同样阻塞了主线程的运行,顺序执行下来。耗时也自然而然的增加了接近两倍。
综上所述,join()的阻塞是不分对象的,与是否守护线程,是否主线程无关,一旦调用,把其他的线程都阻塞起来就完事儿了。只是需要注意,想要真正的多线程运行就要启动所有的子线程后统一调用join,不然就会顺序执行,跟单线程运行没什么区别了。(这里有一个叫做GIL锁的东西可能需要解释一下,但笔者暂时没研究清楚,后续补充)
以上,转载请注明出处,如有错误欢迎批评指正,万分感谢!