python多线程中的守护线程以及join()方法

一、守护线程

要理解守护线程(不分语言),这里有三个问题需要弄清楚:

  • 守护线程顾名思义就是守护别的线程,守护谁呢?一个进程中的主线程,是任何进程中都一定会存在的东西
  • 守护到什么时候呢?主线程运行结束的时候,被守护者都结束退出了,守护者自然没有存在的意义要终止了
  • 与普通子线程有什么区别呢?普通子线程与主线程之间没有谁等待谁结束的关系,竞争地位等同,一个进程中可能是主线程先结束,主线程结束后,整个进程也要等待子线程运行结束后才终止退出。但是如果存在守护线程的话,主线程一旦结束,守护线程也会随之终止,整个进程终止退出,守护线程处于被动地位。也因为这个特点,守护线程一般用作公共服务,比如定时轮询的任务等,而不会用于处理业务逻辑,存储数据等类似工作以防主线程结束后意外终止造成的错误。

笔者表达能力欠佳,下面还是让代码(以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 "主线程运行结束"

下面是运行结果:

python多线程中的守护线程以及join()方法_第1张图片

可以看到主线程运行结束了,子线程还在运行中,但是程序还是坚持等到所有子线程运行完了才退出

下面是一个守护线程的例子:

if __name__ == "__main__":
    print "主线程开始运行"
    for t in threads:
        t.setDaemon(True)   # 设置为守护线程  setDaemon必须在start方法之前完成
        t.start()
    print "主线程运行结束"

运行结果:

python多线程中的守护线程以及join()方法_第2张图片

主线程运行结束但是守护线程没结束的情况下,整个进程直接结束了,守护线程随之终止

二、join()方法的使用

关于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(),以下是运行结果:

python多线程中的守护线程以及join()方法_第3张图片

可以看到,主线程运行结束这句话的打印一直是出现在最后一行的,即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)

运行结果如下:

python多线程中的守护线程以及join()方法_第4张图片

 

结果很明显第一个子线程阻塞了第二个子线程的运行,第二个子线程同样阻塞了主线程的运行,顺序执行下来。耗时也自然而然的增加了接近两倍。

综上所述,join()的阻塞是不分对象的,与是否守护线程,是否主线程无关,一旦调用,把其他的线程都阻塞起来就完事儿了。只是需要注意,想要真正的多线程运行就要启动所有的子线程后统一调用join,不然就会顺序执行,跟单线程运行没什么区别了。(这里有一个叫做GIL锁的东西可能需要解释一下,但笔者暂时没研究清楚,后续补充)

 

以上,转载请注明出处,如有错误欢迎批评指正,万分感谢!

你可能感兴趣的:(多线程)