python笔记——join方法与守护线程setDaemon()

通俗地讲,threading.Thread子类的join方法,是一个用来阻塞主线程的方法。就是就是在某个子线程执行到该语句时,让这个线程执行完毕,才会继续执行其他子线程,当所有调用了join方法的子线程执行结束,主线程才会结束。
而引入了参数timeout,给了每个子线程规定的阻塞时间,为join方法添加了很多用法;另外结合setDaemon()方法,在有无守护进程的场景下战线出了不同作用,以下是结合二者的分析。

1、无join,setDaemon(False)
import time
import threading

class MyThread(threading.Thread):
    def run(self):
        for i in range(5):
            print('thread {}, @number: {}'.format(self.name, i))
            time.sleep(1)


def main():
    print("Start main threading")
    threads = [MyThread() for i in range(3)]
    for t in threads:
        t.start()
    print("End Main threading")

if __name__ == '__main__':
    main()

该模块在main()主线程中,实例化了三个子线程的对象,其中每个子线程的任务是打印五句话,每次打印之间间隔一秒。

Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-3, @number: 0
End Main threading
thread Thread-1, @number: 1
thread Thread-3, @number: 1
thread Thread-2, @number: 1
thread Thread-3, @number: 2
thread Thread-2, @number: 2
thread Thread-1, @number: 2
thread Thread-1, @number: 3
thread Thread-3, @number: 3
thread Thread-2, @number: 3
thread Thread-1, @number: 4
thread Thread-2, @number: 4
thread Thread-3, @number: 4

结果表明,正常情况下,并发执行的三个子线程每一次打印的行为在一秒内完成,主线程也在一秒内完成,子线程剩下的任务则在余下的四秒后完成。

2、无join,setDaemon(True)
def main():
    print("Start main threading")
    threads = [MyThread() for i in range(3)]
    for t in threads:
    	t.setDaemon(True)
        t.start()
    print("End Main threading")

Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-3, @number: 0
End Main threading

此时,只要主线程执行完毕,会立即结束子线程的运行。因为主线程在一秒内执行完,因此只能打印出第一轮循环的结果。

3、有join无参数,setDaemon(False)
def main():
    print("Start main threading")
    threads = [MyThread() for i in range(3)]
    for t in threads:
        t.start()
        t.join()
    print("End Main threading")

Start main threading
thread Thread-1, @number: 0
thread Thread-1, @number: 1
thread Thread-1, @number: 2
thread Thread-1, @number: 3
thread Thread-1, @number: 4
thread Thread-2, @number: 0
thread Thread-2, @number: 1
thread Thread-2, @number: 2
thread Thread-2, @number: 3
thread Thread-2, @number: 4
thread Thread-3, @number: 0
thread Thread-3, @number: 1
thread Thread-3, @number: 2
thread Thread-3, @number: 3
thread Thread-3, @number: 4
End Main threading

def main():
    print("Start main threading")
    threads = [MyThread() for i in range(3)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    print("End Main threading")

Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-3, @number: 0
thread Thread-1, @number: 1
thread Thread-3, @number: 1
thread Thread-2, @number: 1
thread Thread-1, @number: 2
thread Thread-2, @number: 2
thread Thread-3, @number: 2
thread Thread-2, @number: 3
thread Thread-3, @number: 3
thread Thread-1, @number: 3
thread Thread-3, @number: 4
thread Thread-1, @number: 4
thread Thread-2, @number: 4
End Main threading

对比这两种情况:
第一种情况是每个子线程运行后立马调用join方法,这样会使得程序等待该子线程执行完毕后,才会执行下一个子进程,当所有的子线程执行结束后,才会结束主线程。
第二种情况是,三个子线程同时开始执行,执行后依次调用join方法,这样的运行结果和没有join方法时类似,只不过主线程会在所有子线程运行结束后才结束。
那join的作用,是否是只影响调用该方法之后的子线程呢,调用该方法之前就开始运行的子线程不受其影响吗?

def main():
    print("Start main threading")
    threads = [MyThread() for i in range(3)]
    t1 = threads[0]
    t2 = threads[1]
    t3 = threads[2]

    t1.start()
    t2.start()
    t1.join()
    t2.join()

    t3.start()
    t3.join()
    print("End Main threading")

Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-2, @number: 1
thread Thread-1, @number: 1
thread Thread-2, @number: 2
thread Thread-1, @number: 2
thread Thread-2, @number: 3
thread Thread-1, @number: 3
thread Thread-2, @number: 4
thread Thread-1, @number: 4
thread Thread-3, @number: 0
thread Thread-3, @number: 1
thread Thread-3, @number: 2
thread Thread-3, @number: 3
thread Thread-3, @number: 4
End Main threading

结果表明,join阻塞了子线程3和主线程。子线程1和2并行执行,直到结束才开始3的执行。因此,join只影响其后的线程。

4、有join有参数,setDaemon(False)
def main():
    print("Start main threading")
    threads = [MyThread() for i in range(3)]
    for t in threads:
        t.start()
        t.join(2)
    print("End Main threading")

Start main threading
thread Thread-1, @number: 0
thread Thread-1, @number: 1
thread Thread-2, @number: 0
thread Thread-1, @number: 2
thread Thread-2, @number: 1
thread Thread-1, @number: 3
thread Thread-3, @number: 0
thread Thread-2, @number: 2
thread Thread-1, @number: 4
thread Thread-3, @number: 1
thread Thread-2, @number: 3
End Main threading
thread Thread-3, @number: 2
thread Thread-2, @number: 4
thread Thread-3, @number: 3
thread Thread-3, @number: 4

当设置join方法的timeout参数时,主线程会等待所有子线程各自的timeout之和,之后主线程结束,子线程继续执行。(在End Main threading之前,执行了六秒)

def main():
    print("Start main threading")
    threads = [MyThread() for i in range(3)]
    for t in threads:
        t.start()
    for t in threads:
        t.join(1)
    print("End Main threading")

Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-3, @number: 0
thread Thread-3, @number: 1
thread Thread-1, @number: 1
thread Thread-2, @number: 1
thread Thread-3, @number: 2
thread Thread-2, @number: 2
thread Thread-1, @number: 2
End Main threading
thread Thread-1, @number: 3
thread Thread-3, @number: 3
thread Thread-2, @number: 3
thread Thread-1, @number: 4
thread Thread-3, @number: 4
thread Thread-2, @number: 4

即便是在所有子线程开始后,也同样等待六秒,主线程结束,其余子线程继续执行。区别在于子线程的执行顺序,这个顺序和join无参数的情况是一致的。

5、有join无参数,setDaemon(True)
def main():
    print("Start main threading")
    threads = [MyThread() for i in range(3)]
    for t in threads:
    	t.setDaemon(True)
        t.start()
    for t in threads:
        t.join()
    print("End Main threading")

Start main threading
thread Thread-1, @number: 0
thread Thread-2, @number: 0
thread Thread-3, @number: 0
thread Thread-1, @number: 1
thread Thread-2, @number: 1
thread Thread-3, @number: 1
thread Thread-1, @number: 2
thread Thread-2, @number: 2
thread Thread-3, @number: 2
thread Thread-2, @number: 3
thread Thread-3, @number: 3
thread Thread-1, @number: 3
thread Thread-2, @number: 4
thread Thread-3, @number: 4
thread Thread-1, @number: 4
End Main threading

守护线程只作用于主线程,上述代码的主线程是当所有子线程执行完毕后执行完,因此从结果来看,并无差异。
同样,在每个子线程开始后立马执行无参数join语句,因为主线程本身就是最后结束,因此守护线程情况下,效果也无差异。(省略代码以及结果)

6、有join有参数,setDaemon(True)
def main():
    print("Start main threading")
    threads = [MyThread() for i in range(3)]
    for t in threads:
    	t.setDaemon(True)
        t.start()
        t.join(1)
    print("End Main threading")

Start main threading
thread Thread-1, @number: 0
thread Thread-1, @number: 1
thread Thread-2, @number: 0
thread Thread-1, @number: 2
thread Thread-2, @number: 1
thread Thread-3, @number: 0
thread Thread-1, @number: 3
End Main threading
thread Thread-3, @number: 1

每个子线程开始后立即执行join一秒的语句,当他们的时间之和结束后,主线程结束,线程没有执行完毕的部分被强制杀死。另一种情况也是如此。
这里需要注意的是,End语句打印后又打印了一句,而另一种join位置也是如此,我分析,这是因为执行print这句话的时候,已经是第四秒开始,主线程结束这句话所消耗的时间,子线程会继续执行,直到真正终止的时刻。因此会有一些超过三秒的打印语句被打印的情况。

总结

join的主要目的就是阻塞主线程
join无参数时,join阻塞了其语句之后才开始的子线程和主线程
join有参数时:

  1. 守护线程时,主线程等待所有子线程join的timeout时间之和,之后杀死所有子线程,退出程序。
  2. 非守护线程时,主线程等待所有子线程timeout时间之和,之后主线程结束,子线程继续执行直到结束。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值