• join和 Daemon守护线程


    一、前言

      一个程序至少有一个主线程,主线程启动子线程后,它们之间并没有隶属关系。主线程和子线程执行是并行的,相互独立。主线程执行完毕后默认不等子线程执行结束就接着往下走了,如果有其他程序就会运行另外的程序,如果没有就等待子线程执行完成后结束程序。

    import threading
    import time
    import random
    
    
    class MyThread(threading.Thread):
    
        def __init__(self, n):
            super(MyThread, self).__init__()
            self.n = n
    
        def run(self):
            print('task %s is operating' % self.n)
            t_time = random.randint(1, 8)
            time.sleep(t_time)
           # getNname获取线程名称
            print(self.getName(), 'finished', 'I sleep %d seconds' % t_time)
    
    
    class Person(object):
    
        def __init__(self, name):
            self.name = name
    
        def get_info(self):
            # time.sleep(10)
            print('my name is %s.' % self.name)
    
    if __name__ == '__main__':
    
        start_time = time.time()
        for i in range(5):
            t = MyThread(i)
            t.start()
        # t.join()
        print('main thread finished.')
        print('*****执行另一个程序了******')
        p = Person('bigberg')
        p.get_info()
        print('*************************')
        print('cost: %s' % (time.time() - start_time))
    

      结果:

    # 线程开始执行
    
    task 0 is operating
    task 1 is operating
    task 2 is operating
    task 3 is operating
    task 4 is operating
    # 主线程执行完毕
    main thread finished.
    
    # 可以执行另一个程序了
    *****执行另一个程序了******
    my name is bigberg.
    *************************
    # 这里花费的时间是主线程的运行时间,显然没有计算子线程的时间
    cost: 0.0019881725311279297
    
    # 子线程在主线程结束后依然在运行
    Thread-3 finished I sleep 2 seconds
    Thread-5 finished I sleep 2 seconds
    Thread-2 finished I sleep 3 seconds
    Thread-4 finished I sleep 4 seconds
    Thread-1 finished I sleep 5 seconds
    
    # 所有子线程完毕后程序结束
    Process finished with exit code 0
    

      

    二、join 等待子线程完成

      如果在线程实例后加上join默认主线程是阻塞的,主线程会等待该子线程运行完成后在结束。

    # -*- coding: UTF-8 -*-
    
    import threading
    import time
    import random
    
    
    class MyThread(threading.Thread):
    
        def __init__(self, n):
            super(MyThread, self).__init__()
            self.n = n
    
        def run(self):
            print('task %s is operating' % self.n)
            t_time = random.randint(1, 8)
            time.sleep(t_time)
            print(self.getName(), 'finished', 'I sleep %d seconds' % t_time)
    
    
    if __name__ == '__main__':
    
        start_time = time.time()
        for i in range(5):
            t = MyThread(i)
            t.start()
            t.join()    # 添加join,阻塞主线程
        print('main thread finished.')
        print('cost: %s' % (time.time() - start_time))
    
    # 注
    # 如果对每个线程都加join,那么并发就没有了,实际上线程都是串行的
    # 前一个线程执行完了,才会执行下一个线程
    # 主线程最后运行完毕
    

      结果:

    task 0 is operating
    Thread-1 finished I sleep 2 seconds
    task 1 is operating
    Thread-2 finished I sleep 6 seconds
    task 2 is operating
    Thread-3 finished I sleep 4 seconds
    task 3 is operating
    Thread-4 finished I sleep 8 seconds
    task 4 is operating
    Thread-5 finished I sleep 5 seconds
    # 这里主线程已经是最后执行完毕的了
    main thread finished.
    
    # 消耗的时间也是每个线程的运行时间之和
    cost: 25.005265712738037
    

      2.1 计算并发运行时间

      如果不想计算出总的运行时间,而是所有线程的并发运行时间呢?就像上例中的那样,最长运行时间是8秒,那么所有线程都能在8秒内全部运行完毕。

      把t.join()单独移到for循环外面是不行的,因为这样并发运行总会在最后一个线程出阻塞。如下:  

    # -*- coding: UTF-8 -*-
    
    import threading
    import time
    import random
    
    
    class MyThread(threading.Thread):
    
        def __init__(self, n):
            super(MyThread, self).__init__()
            self.n = n
    
        def run(self):
            print('task %s is operating' % self.n)
            t_time = random.randint(1, 8)
            time.sleep(t_time)
            print(self.getName(), 'finished', 'I sleep %d seconds' % t_time)
    
    
    if __name__ == '__main__':
    
        start_time = time.time()
        for i in range(5):
            t = MyThread(i)
            t.start()
        t.join()    # 添加join,阻塞主线程
        print('main thread finished.')
        print('cost: %s' % (time.time() - start_time))
    

      结果:

    task 0 is operating
    task 1 is operating
    task 2 is operating
    task 3 is operating
    task 4 is operating
    Thread-1 finished I sleep 2 seconds
    Thread-3 finished I sleep 2 seconds
    Thread-5 finished I sleep 3 seconds
    # 其实是在线程5,也就是最后一个线程出阻塞的
    main thread finished.
    cost: 3.001293659210205
    Thread-2 finished I sleep 4 seconds
    Thread-4 finished I sleep 5 seconds
    

      正确的方法,定义一个空列表,获取所以的线程实例,for 循环阻塞所有的线程实例 

    # -*- coding: UTF-8 -*-
    
    import threading
    import time
    import random
    
    
    class MyThread(threading.Thread):
    
        def __init__(self, n):
            super(MyThread, self).__init__()
            self.n = n
    
        def run(self):
            print('task %s is operating' % self.n)
            t_time = random.randint(1, 8)
            time.sleep(t_time)
            print(self.getName(), 'finished', 'I sleep %d seconds' % t_time)
    
    
    if __name__ == '__main__':
        t_list = []
        start_time = time.time()
        for i in range(5):
            t = MyThread(i)
            t.start()
            t_list.append(t)
    
        for t in t_list:
            t.join()
        print('main thread finished.')
        print('cost: %s' % (time.time() - start_time))
    

      结果,事实上也符合我们刚才的推论,运行时间最长的线程所消耗的时间,就是总的并发时间 

    task 0 is operating
    task 1 is operating
    task 2 is operating
    task 3 is operating
    task 4 is operating
    Thread-3 finished I sleep 3 seconds
    Thread-5 finished I sleep 3 seconds
    Thread-2 finished I sleep 7 seconds
    Thread-1 finished I sleep 7 seconds
    Thread-4 finished I sleep 8 seconds
    main thread finished.
    cost: 8.001787185668945
    
    # 并发时间在8秒左右
    

      总结:主线程创建一个子线程后,如果子线程调用join()方法,主线程会在调用的地方等待,直到该子线程运行完成才会接着往下执行。

    三、守护线程setDaemon

      setDaemon()方法:在主线程中创建子线程,该子线程调用setDaemon方法后成为主线程的守护线程。这种情况下如果主线程执行结束,那么不管子线程是否完成,一并和主线程退出。这里基本和join()方法相反。此外,还有个要特别注意的:必须在start() 方法调用之前设置,如果不设置为守护线程,程序会被无限挂起。  

    # -*- coding: UTF-8 -*-
    
    import threading
    import time
    import random
    
    
    class MyThread(threading.Thread):
    
        def __init__(self, n):
            super(MyThread, self).__init__()
            self.n = n
    
        def run(self):
            print('task %s is operating' % self.n)
            t_time = random.randint(1, 8)
            time.sleep(t_time)
            print(self.getName(), 'finished', 'I sleep %d seconds' % t_time)
    
    
    if __name__ == '__main__':
    
        start_time = time.time()
        for i in range(5):
            t = MyThread(i)
            t.setDaemon(True)
            t.start()
        print('main thread finished.', threading.current_thread(), threading.active_count())
        print('cost: %s' % (time.time() - start_time))
    

      注:threading.current_thread()查看当前运行的线程

        threading.active_count() 查看活跃线程数

        线程数 =  主线程 + 子线程数

      结果: 

    task 0 is operating
    task 1 is operating
    task 2 is operating
    task 3 is operating
    task 4 is operating
    main thread finished. <_MainThread(MainThread, started 8656)> 6
    cost: 0.0009999275207519531
    
    Process finished with exit code 0
    
    # 很显然把子线程设置为主线程的守护线程后,主线程一旦结束,程序就执行退出运行,不会再等待子线程运行。
    

      注:如果程序中有其他非守护线程时,还是会等待非守护线程运行完毕,程序才会结束。

  • 相关阅读:
    IDEA与Eclipse
    解释器模式
    设计模式(十一)—— 策略模式
    设计模式(六)—— 装饰模式
    Java注解
    Spring源码阅读(二)—— AOP
    业务开发(八)—— Maven
    高性能MySQL笔记
    Java源码阅读(六)—— ReentrantLock
    业务开发(六)—— MyBatis框架
  • 原文地址:https://www.cnblogs.com/bigberg/p/7815443.html
Copyright © 2020-2023  润新知