一文说懂join和setDaemon
最精简的知识点:
1、主线程退出,子线程还可以在跑。线程之间都无关
2、守护线程会被其他最后一个线程或主线程终止。监听谁 就把谁设置守护线程
3、想在主线程结束前跑完子线程,用join().
----------------------------------
这里有个小疑问,既然加不加join子线程都会跑完,为什么还加join。因为有些线程之间需要输出参数给其余函数用,所以得等某个函数线程跑完才能执行主线程。
1 先记住三句话
主线程退出,进程等待所有子线程执行完毕后才结束
主线程结束后进程不等待守护线程完成,立即结束 setDaemon(True)
主线程等待子线程完成后结束 join
2 作用
1)想先停主线程,先跑子现程用join
2)想监听某个函数,超时后关闭这个函数,设立这个函数的线程为守护线程。
3)最常见的连用方式 thr.setDaemon(True), 后面thr.join() 注意不用加时间。或者加一个超时时间,超时时间一到主线程执行,执行结束后,进程自动关闭守护子线程。
仔细理解下面两个功能代码:
1 超时器
import time #from threading import Thread import threading import sys class KThread(threading.Thread): """A subclass of threading.Thread, with a kill() method. Come from: Kill a thread in Python: http://mail.python.org/pipermail/python-list/2004-May/260937.html """ def __init__(self, *args, **kwargs): threading.Thread.__init__(self, *args, **kwargs) self.killed = False def start(self): """Start the thread.""" self.__run_backup = self.run self.run = self.__run # Force the Thread to install our trace. threading.Thread.start(self) def __run(self): """Hacked run function, which installs the trace.""" sys.settrace(self.globaltrace) self.__run_backup() self.run = self.__run_backup def globaltrace(self, frame, why, arg): if why == 'call': return self.localtrace else: return None def localtrace(self, frame, why, arg): if self.killed: if why == 'line': raise SystemExit() return self.localtrace def kill(self): self.killed = True class Timeout(Exception): """function run timeout""" def timeout(seconds = 3): def timeout_decorator(func): def _new_func(oldfunc, result, oldfunc_args, oldfunc_kwargs): result.append(oldfunc(*oldfunc_args, **oldfunc_kwargs)) def _(*args, **kwargs): result = [] new_kwargs = { # create new args for _new_func, because we want to get the func return val to result list 'oldfunc': func, 'result': result, 'oldfunc_args': args, 'oldfunc_kwargs': kwargs } thd = KThread(target=_new_func, args=(), kwargs=new_kwargs) thd.setDaemon(True) thd.start() thd.join(seconds) #alive = thd.isAlive() 版本isAlive转为is_alive alive = thd.is_alive() thd.kill() #try: #if alive: #raise Timeout(u'function run too long, timeout %d seconds.' % seconds) #else: #if len(result) == 0: #raise Exception("TESTCASE FAILED") #else: #return result[0] #except Exception as e: #print("toolong",e) if alive: raise Timeout(u'function run too long, timeout %d seconds.' % seconds) else: if len(result) == 0: raise Exception("TESTCASE FAILED") else: return result[0] _.__name__ = func.__name__ _.__doc__ = func.__doc__ return _ return timeout_decorator @timeout(3) def fun(): print("111231111111---") time.sleep(100) print("===") @timeout(3) def fun0(): print("11111") time.sleep(2) print("end") return 10001 #fun() #print(fun0()) def tr(sce): def wrap(fun): def deco(): print("-=====", sce) fun() print("_==========") return deco return wrap @tr(3) def runt(): print("run111111111===",runt.__name__) #runt()
2 监听某个目录下文件是否生成
import os import time import threading RET=0 def wrap(func): def lis_file(dirname,name,epch=30,tim=0.1): print("===lis_file") print("epch===",epch) global RET print("RET===",RET) c=1 while c<=epch: print("=== %s==="%c) file_list = os.listdir(dirname) if name in file_list: RET=True print("===",RET) return RET time.sleep(tim) c+=1 print("c=====",c) RET=False return RET def _(*args): lis = threading.Thread(target=lis_file,args=(args)) ft = threading.Thread(target=func,args=(args)) #lis.setDaemon(False) # 想设立守护线程,即哪个函数需要被监控就设立哪个函数为守护现程。 # 这个守护现程一定掩护所有现程子主线程全部跑完才能结束。 ft.setDaemon(True) lis.start() ft.start() #func(*args) # join加入容器,会先跑容器中的线程,然后再回到主线程 # join 1 表示主线程等子线程1s后结束,然后子线程还继续跑 # 画出现程关系图 join为主现程一直等待子线程 # 不叫join 为 主线程跑 子线程跑 子现程跑 主线程结束,子现程结束 ft.join() print("============") return _ @wrap def fun(dirname,name,epch=30,tim=0.1): print("start===") time.sleep(10) with open("shiyan.txt","w") as f: f.write("===") print("end===") d=r"C:\Users\devevf\Documents\AItest\shiyan" name=r"shiyan.txt" fun(d,name,30,0.1)
3 为了验证setDaemon
注意 跑带有setDaemon时一定在cmd执行py脚本,在ide中没有setDaemon效果
import threading import time def child_thread1(): for i in range(10): time.sleep(1) with open("setd111.txt","a") as f: f.write(str(i)) print('child_thread1_running...') def child_thread2(): with open("setd222.txt","a") as f: for i in range(1): time.sleep(1) f.write(str(i)) print('child_thread2_running...') def parent_thread(): print('parent_thread_running...') thread1 = threading.Thread(target=child_thread1) thread2 = threading.Thread(target=child_thread2) # 注意ide体现不出来,在cmd里面执行会明显 # 守护线程,当前进程结束线程立即结束,或者是主线程结束立即结束 # 主线程结束后进程不等待守护线程完成,立即结束 # 实验为 守护线程或长或短都不影响主线程的结束 # 守护线程 一定等所有线程结束才能结束 # 两个守护线程 按照谁的进度,谁都不按照,只是按照主线程 thread1.setDaemon(True) #thread2.setDaemon(True) thread1.start() thread2.start() print('parent_thread_exit...') time.sleep(1) with open("main_file.txt","w") as f: f.write("mainfile") print("main bu shou yingxiang====yanzheng kongzhi jincheng") if __name__ == "__main__": parent_thread()
---------------------------------------------------------------------------------------------------------
参考下文,若精炼内容没有理解清楚请看参考,例子都很经典。
python主线程与子线程的结束顺序
引用自 主线程退出对子线程的影响--YuanLi 的一段话:
对于程序来说,如果主进程在子进程还未结束时就已经退出,那么Linux内核会将子进程的父进程ID改为1(也就是init进程),当子进程结束后会由init进程来回收该子进程。
主线程退出后子线程的状态依赖于它所在的进程,如果进程没有退出的话子线程依然正常运转。如果进程退出了,那么它所有的线程都会退出,所以子线程也就退出了。
主线程退出,进程等待所有子线程执行完毕后才结束
进程启动后会默认产生一个主线程,默认情况下主线程创建的子线程都不是守护线程(setDaemon(False))。因此主线程结束后,子线程会继续执行,进程会等待所有子线程执行完毕后才结束
所有线程共享一个终端输出(线程所属进程的终端)
import threading
import time
def child_thread1():
for i in range(100):
time.sleep(1)
print('child_thread1_running...')
def parent_thread():
print('parent_thread_running...')
thread1 = threading.Thread(target=child_thread1)
thread1.start()
print('parent_thread_exit...')
if __name__ == "__main__":
parent_thread()
输出为:
parent_thread_running...
parent_thread_exit...
child_thread1_running...
child_thread1_running...
child_thread1_running...
child_thread1_running...
...
可见父线程结束后,子线程仍在运行,此时结束进程,子线程才会被终止
主线程结束后进程不等待守护线程完成,立即结束
当设置一个线程为守护线程时,此线程所属进程不会等待此线程运行结束,进程将立即结束
import threading
import time
def child_thread1():
for i in range(100):
time.sleep(1)
print('child_thread1_running...')
def child_thread2():
for i in range(5):
time.sleep(1)
print('child_thread2_running...')
def parent_thread():
print('parent_thread_running...')
thread1 = threading.Thread(target=child_thread1)
thread2 = threading.Thread(target=child_thread2)
thread1.setDaemon(True)
thread1.start()
thread2.start()
print('parent_thread_exit...')
if __name__ == "__main__":
parent_thread()
输出:
parent_thread_running...
parent_thread_exit...
child_thread1_running...child_thread2_running...
child_thread1_running...child_thread2_running...
child_thread1_running...child_thread2_running...
child_thread1_running...child_thread2_running...
child_thread2_running...child_thread1_running...
Process finished with exit code 0
thread1是守护线程,thread2非守护线程,因此,进程会等待thread2完成后结束,而不会等待thread1完成
注意:子线程会继承父线程中daemon的值,即守护线程开启的子线程仍是守护线程
主线程等待子线程完成后结束
在线程A中使用B.join()表示线程A在调用join()处被阻塞,且要等待线程B的完成才能继续执行
import threading
import time
def child_thread1():
for i in range(10):
time.sleep(1)
print('child_thread1_running...')
def child_thread2():
for i in range(5):
time.sleep(1)
print('child_thread2_running...')
def parent_thread():
print('parent_thread_running...')
thread1 = threading.Thread(target=child_thread1)
thread2 = threading.Thread(target=child_thread2)
thread1.setDaemon(True)
thread2.setDaemon(True)
thread1.start()
thread2.start()
thread2.join()
1/0
thread1.join()
print('parent_thread_exit...')
if __name__ == "__main__":
parent_thread()
输出:
parent_thread_running...
child_thread1_running...
child_thread2_running...
child_thread1_running...
child_thread2_running...
child_thread1_running...
child_thread2_running...
child_thread1_running...
child_thread2_running...
child_thread1_running...
child_thread2_running...
Traceback (most recent call last):
File "E:/test_thread.py", line 31, in <module>
parent_thread()
File "E:/test_thread.py", line 25, in parent_thread
1/0
ZeroDivisionError: integer division or modulo by zero
主线程在执行到thread2.join()时被阻塞,等待thread2结束后才会执行下一句
1/0
会使主线程报错退出,且thread1设置了daemon=True,因此主线程意外退出时thread1也会立即结束。thread1.join()没有被主线程执行