python动态线程池 python线程池join_主线程


概念补充:

  • 在Python多线程编程中,join方法的作用是线程同步
  • 守护线程,是为守护别人而存在,当设置为守护线程后,被守护的主线程不存在后,守护线程也自然不存在

以下分5种不同的形式解释join在多线程编程中的用处

第一种:Python多线程默认情况

Python多线程的默认情况(设置线程setDaemon(False)),主线程执行完自己的任务以后,就退出了,此时子线程会继续执行自己的任务,直到自己的任务结束


import threading, time
def doWaiting1():
    print('start waiting1: ' + time.strftime('%H:%M:%S') + "\n")
    time.sleep(3)
    print("线程1奉命报道")
    print('stop waiting1: ' + time.strftime('%H:%M:%S') + "\n")
def doWaiting2():
    print('start waiting2: ' + time.strftime('%H:%M:%S') + "\n")
    time.sleep(8)
    print("线程2奉命报道")
    print('stop waiting2: ', time.strftime('%H:%M:%S') + "\n")

tsk = []

# 创建并开启线程1
thread1 = threading.Thread(target = doWaiting1)
thread1.start()
tsk.append(thread1)

# 创建并开启线程2
thread2 = threading.Thread(target = doWaiting2)
thread2.start()
tsk.append(thread2)

# 计时程序
print('start join: ' + time.strftime('%H:%M:%S')  )
print('end join: ' + time.strftime('%H:%M:%S') )

运行结果

start waiting1: 20:03:30
start waiting2: 20:03:30

start join: 20:03:30
end join: 20:03:30

线程1奉命报道
stop waiting1: 20:03:33

线程2奉命报道
stop waiting2:  20:03:38

结论:
1、计时程序属于主线程,整个主线程在开启线程1和线程2后,进入计时模块,主线程结束
2、主线程结束,但并没有影响线程1和线程2的运行,故后面线程1和线程2仍然跑来报道,至此整个程序才完全结束

第二种:开启守护线程

开启线程的setDaemon(True),设置子线程为守护线程,实现主程序结束,子程序立马全部结束功能


import threading, time
def doWaiting1():
    print('start waiting1: ' + time.strftime('%H:%M:%S') + "\n")
    time.sleep(3)
    print("线程1奉命报道")
    print('stop waiting1: ' + time.strftime('%H:%M:%S') + "\n")
def doWaiting2():
    print('start waiting2: ' + time.strftime('%H:%M:%S') + "\n")
    time.sleep(8)
    print("线程2奉命报道")
    print('stop waiting2: ', time.strftime('%H:%M:%S') + "\n")

tsk = []

# 创建并开启线程1
thread1 = threading.Thread(target = doWaiting1)
thread1.setDaemon(True)
thread1.start()
tsk.append(thread1)

# 创建并开启线程2
thread2 = threading.Thread(target = doWaiting2)
thread2.setDaemon(True)
thread2.start()
tsk.append(thread2)

print('start join: ' + time.strftime('%H:%M:%S')  )
print('end join: ' + time.strftime('%H:%M:%S') )

运行结果:

start waiting1: 20:10:04
start waiting2: 20:10:04

start join: 20:10:04
end join: 20:10:04

结论:
1、主线程结束后,无论子线程1,2是否运行完成,都结束不再往下继续运行

第三种:加入join方法设置同步

当不给程序设置守护进程时,主程序将一直等待子程序全部运行完成才结束


import threading, time
def doWaiting1():
    print('start waiting1: ' + time.strftime('%H:%M:%S') + "\n")
    time.sleep(3)
    print("线程1奉命报道")
    print('stop waiting1: ' + time.strftime('%H:%M:%S') + "\n")
def doWaiting2():
    print('start waiting2: ' + time.strftime('%H:%M:%S') + "\n")
    time.sleep(8)
    print("线程2奉命报道")
    print('stop waiting2: ', time.strftime('%H:%M:%S') + "\n")

tsk = []

# 创建并开启线程1
thread1 = threading.Thread(target = doWaiting1)
thread1.start()
tsk.append(thread1)

# 创建并开启线程2
thread2 = threading.Thread(target = doWaiting2)
thread2.start()
tsk.append(thread2)

print('start join: ' + time.strftime('%H:%M:%S')  )
for t in tsk:
    print('%s线程到了'%t)
    t.join()
print('end join: ' + time.strftime('%H:%M:%S') )

运行结果:

start waiting1: 20:14:35
start waiting2: 20:14:35

start join: 20:14:35

<Thread(Thread-1, started 19648)>线程到了
线程1奉命报道
stop waiting1: 20:14:38

<Thread(Thread-2, started 24056)>线程到了
线程2奉命报道
stop waiting2:  20:14:43

end join: 20:14:43

结论:
1、使用join函数,主线程将被阻塞,一直等待被使用了join方法的线程运行完成
2、start join 是在35秒,stop waiting1在38秒,刚好sleep了3秒,stop waiting2是43秒,刚好sleep了8秒,这也说明,线程1和2是基本同时运行的,但由于执行所消耗的时间不一致,所以阻塞所用的时间也是不一样的,最终end join时间是最后线程运行完,整个程序就中止在43秒
3、将所有的线程放入一个列表,通过循环对列表中的所有线程使用join方法判断,也是为了保证全部子线程都能全部运行完成,主线程才退出

第四种:不设置守护进程但join设置超时

给join设置timeout数值,判断等待多后子线程还没有完成,则主线程不再等待


import threading, time
def doWaiting1():
    print('start waiting1: ' + time.strftime('%H:%M:%S') + "\n")
    time.sleep(2)
    print("线程1奉命报道")
    print('stop waiting1: ' + time.strftime('%H:%M:%S') + "\n")
def doWaiting2():
    print('start waiting2: ' + time.strftime('%H:%M:%S') + "\n")
    time.sleep(8)
    print("线程2奉命报道")
    print('stop waiting2: ', time.strftime('%H:%M:%S') + "\n")

tsk = []

# 创建并开启线程1
thread1 = threading.Thread(target = doWaiting1)
thread1.start()
tsk.append(thread1)

# 创建并开启线程2
thread2 = threading.Thread(target = doWaiting2)
thread2.start()
tsk.append(thread2)

print('start join: ' + time.strftime('%H:%M:%S')  )
for t in tsk:
    print("开始:"+time.strftime('%H:%M:%S'))
    print('%s线程到了'%t)
    t.join(5)
    print("结束:" + time.strftime('%H:%M:%S'))
print('end join: ' + time.strftime('%H:%M:%S') )

运行结果:

start waiting1: 21:14:25
start waiting2: 21:14:25

start join: 21:14:25

开始:21:14:25
<Thread(Thread-1, started 22348)>线程到了
线程1奉命报道
stop waiting1: 21:14:27
结束:21:14:27

开始:21:14:27
<Thread(Thread-2, started 13164)>线程到了
结束:21:14:32

end join: 21:14:32

线程2奉命报道
stop waiting2:  21:14:33

结论:
1、给join设置等待时间后,超过了等待时间后,主线程终止,但不影响子线程继续运行,等子线程全部运行完毕整个程序终止
2、所有线程可用的等待时间 timeout_total ≤ timeout * 线程数量
3、虽然timeout设置的是5s,但是线程1只需要2s,所以循环从开始到结束,只需消耗2s(21:14:27 - 21:14:25),到此循环就进入第二次,第二次等待仍可以分配5s(21:14:32 - 21:14:27),所以两次总共的等待是时间2+5=7s,但是线程2运行所需要时间是8s,而且8s是从21:14:25开始的,结束时间是21:14:33,因为join等待时间完了主程序结束了,但不影响线程2继续运行,所以在end join后,线程2仍然输出了报道结果(是因为没有开启守护线程)
4、个别文章解释join这个时间为:主线程会等待多个线程的timeout累加和,这个说法不准确,由3的推理可以得出,并非会一定等待“线程数* timeout”这么多时间,而是≤“线程数*timeout”,

第五种:开启守护线程,同事给join设置超时

超时未处理完毕的子线程将被直接终止


import threading, time
def doWaiting1():
    print('start waiting1: ' + time.strftime('%H:%M:%S') + "\n")
    time.sleep(2)
    print("线程1奉命报道")
    print('stop waiting1: ' + time.strftime('%H:%M:%S') + "\n")
def doWaiting2():
    print('start waiting2: ' + time.strftime('%H:%M:%S') + "\n")
    time.sleep(8)
    print("线程2奉命报道")
    print('stop waiting2: ', time.strftime('%H:%M:%S') + "\n")

tsk = []

# 创建并开启线程1
thread1 = threading.Thread(target = doWaiting1)
thread1.setDaemon(True)
thread1.start()
tsk.append(thread1)

# 创建并开启线程2
thread2 = threading.Thread(target = doWaiting2)
thread2.setDaemon(True)
thread2.start()
tsk.append(thread2)

print('start join: ' + time.strftime('%H:%M:%S')  )
for t in tsk:
    print("开始:"+time.strftime('%H:%M:%S'))
    print('%s线程到了'%t)
    t.join(5)
    print("结束:" + time.strftime('%H:%M:%S'))
print('end join: ' + time.strftime('%H:%M:%S') )

运行结果:

start waiting1: 21:24:14
start waiting2: 21:24:14

start join: 21:24:14

开始:21:24:14
<Thread(Thread-1, started daemon 9060)>线程到了
线程1奉命报道
stop waiting1: 21:24:16
结束:21:24:16

开始:21:24:16
<Thread(Thread-2, started daemon 13912)>线程到了
结束:21:24:21

end join: 21:24:21

结论:
1、相比第四种,超时后主线程运行到end join则结束了,子线程2已经被终止停止运行