Thread实例对象的方法 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程对象。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程个数,与len(threading.enumerate())有相同的结果。 #threading.main_thread() 返回主线程对象 #threading.get_ident() 返回当前线程的ID,非0整数
例子python
import time import threading def func(arg): time.sleep(1) print(arg,threading.current_thread(),threading.get_ident()) #threading.current_thread() 获取当前进程对象, # threading.get_ident()获取当前线程号 for i in range(10): threading.Thread(target=func,args=(i,)).start() print("线程数量统计",threading.active_count()) #统计当前线程数量 threading.current_thread().setName("主线程") #设置线程名字 print(threading.current_thread().isAlive()) #线程是否是活动的 print("当前线程",threading.current_thread()) print("获取当前线程名字",threading.current_thread().getName()) print("线程变量列表",threading.enumerate()) #以列表的形式显示当前全部的线程变量
与进程的join方法做用相似,线程的 join方法的做用是阻塞,等待子线程结束,join方法有一个参数是timeout,即若是主线程等待timeout,子线程尚未结束,则主线程强制结束子线程。bootstrap
可是python 默认参数建立线程后,无论主线程是否执行完毕,都会等待子线程执行完毕才一块儿退出,有无join结果同样。进程没有join()则在执行主进程完后直接退出,而主线程是等待子线程执行完毕才一块儿退出。多线程
import threading import time def func(n): time.sleep(2) print("线程是%s"%n) global g g = 0 print(g) if __name__ == '__main__': g = 100 t_l = [] for i in range(5): t = threading.Thread(target=func,args=(i,)) t.start() t_l.append(t) print("线程数量统计1--", threading.active_count()) # 统计当前线程数量,结果是6,5个子线程加1个主线程 for t in t_l: t.join() print('结束了') print("线程数量统计2--", threading.active_count()) # 统计当前线程数量,结果是1,只有一个主线程
import threading import time def add(x, y): for _ in range(5): # _解压序列赋值,_表明不用关心的元素 time.sleep(0.5) print("x+y={}".format(x + y)) class MyThread(threading.Thread): def start(self): print('start-----') super().start() # 调用父类的start()和run()方法 def run(self): print('run-----') super().run() # 调用父类的start()和run()方法 t = MyThread(target=add, name="MyThread", args=(1, 2)) t.start() # t.run() print("====end===")
执行结果:app
start----- run----- ====end=== x+y=3 x+y=3 x+y=3 x+y=3 x+y=3
分析:能够看出start()方法会先运行start()方法,再运行run()方法。ide
从源码简单追踪下start()的调用过程:函数
一、 def start(self): print('start-----') super().start() # 调用父类的start()和run()方法 二、def start(self): #父类的start() _start_new_thread(self._bootstrap, ()) #执行_start_new_thread找到_start_new_thread,再次找到_thread.start_new_thread,这里是pass #下一步获取self._bootstrap值找到def _bootstrap,经过self._bootstrap_inner(),最后执行了 #self.run() .... 三、_start_new_thread = _thread.start_new_thread 四、def start_new_thread(function, args, kwargs=None): pass 五、def _bootstrap(self): self._bootstrap_inner() 六、def _bootstrap_inner(self): .... try: self.run()#最终start()方法调用了run()方法 except SystemExit: pass
import threading import time def add(x, y): for _ in range(5): # _解压序列赋值,_表明不用关心的元素 time.sleep(0.5) print("x+y={}".format(x + y)) class MyThread(threading.Thread): def start(self): print('start-----') super().start() # 调用父类的start()和run()方法 def run(self): print('run-----') super().run() # 调用父类的start()和run()方法 t = MyThread(target=add, name="MyThread", args=(1, 2)) # t.start() t.run() print("====end===")
执行结果:spa
run----- x+y=3 x+y=3 x+y=3 x+y=3 x+y=3 ====end===
分析:运行线程的run()方法只能调用到run()方法。操作系统
从源码简单追踪下runt()的调用过程:线程
一、def run(self): print('run-----') super().run() # 调用父类的start()和run()方法 二、def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None): self._target = target #这里的_target是个子线程的函数名 self._args = args self._kwargs = kwargs .... 三、def run(self): if self._target: self._target(*self._args, **self._kwargs) #这里就直接执行了这个函数
分析:target是咱们传入的目标函数,run()方法其实就相似一个装饰器,最终仍是将args 和kwargs 参数传入目标函数运行,返回结果。orm
import threading import time def func(n): time.sleep(2) print("线程是%s" % n) print('子线程的ID号A', threading.current_thread().ident) global g g = 0 print('子线程中的g', g) class Mythread(threading.Thread): def __init__(self, arg, *args, **kwargs): super().__init__(*args, **kwargs) self.arg = arg def start(self): print('start-----') super().start() # 调用父类的start()和run()方法 def run(self): print('run-----') print("类中的子线程", self.arg) super().run() print('子线程的ID号B',threading.current_thread().ident) if __name__ == '__main__': g = 100 t1 = Mythread('hello', target=func, name="MyThread", args=('nick',)) # 第一个参数是用在Mythread类中的,后面的3个参数用在建立的func子线程中,args必须是可迭代的 # 这里的func也能够直接写在Mythread中的run()里,这时这里的run()不用再继承父类的run() t1.start() #t1.run() print('主线程中的g', g) print('主线程的ID号---', threading.current_thread().ident)
执行结果
start----- run----- 类中的子线程 hello 线程是nick 子线程的ID号A 19672 子线程中的g 0 子线程的ID号B 19672 主线程中的g 0 主线程的ID号--- 12056
分析:能够看到这里有主进程有子线程func()和mythread.run()属于同一子线程,由于mythread.run()继承父类的run()最终仍是要执行func()函数的,这里只是在对象中多写了几行。
import threading import time def func(n): time.sleep(2) print("线程是%s" % n) print('子线程的ID号A', threading.current_thread().ident) global g g = 0 print('子线程中的g', g) class Mythread(threading.Thread): def __init__(self, arg, *args, **kwargs): super().__init__(*args, **kwargs) self.arg = arg def start(self): print('start-----') super().start() # 调用父类的start()和run()方法 def run(self): print('run-----') print("类中的子线程", self.arg) super().run() print('子线程的ID号B',threading.current_thread().ident) if __name__ == '__main__': g = 100 t1 = Mythread('hello', target=func, name="MyThread", args=('nick',)) # 第一个参数是用在Mythread类中的,后面的3个参数用在建立的func子线程中,args必须是可迭代的 # 这里的func也能够直接写在Mythread中的run()里,这时这里的run()不用再继承父类的run() # t1.start() t1.run() print('主线程中的g', g) print('主线程的ID号---', threading.current_thread().ident)
执行结果
run----- 类中的子线程 hello 线程是nick 子线程的ID号A 18332 子线程中的g 0 子线程的ID号B 18332 主线程中的g 0 主线程的ID号--- 18332
分析:这能够看到,程序居然只有有个线程,那就是主线程。
例子
import threading # 定义准备做为子线程action函数 def action(max): for i in range(max): # 直接调用run()方法时,Thread的name属性返回的是该对象的名字 # 而不是当前线程的名字 # 使用threading.current_thread().name老是获取当前线程的名字 print(threading.current_thread().name + " " + str(i)) # ① for i in range(100): # 调用Thread的currentThread()方法获取当前线程 print(threading.current_thread().name + " " + str(i)) if i == 20: # 直接调用线程对象的run()方法 # 系统会把线程对象当成普通对象,把run()方法当成普通方法 # 因此下面两行代码并不会启动两个线程,而是依次执行两个run()方法 threading.Thread(target=action,args=(100,)).run() threading.Thread(target=action,args=(100,)).run()
上面程序在建立线程对象后,直接调用了线程对象的 run() 方法,程序运行的结果是整个程序只有一个主线程。还有一点须要指出,若是直接调用线程对象的 run() 方法,则在 run() 方法中不能直接经过 name 属性(getName() 方法)来获取当前执行线程的名字,而是须要使用 threading.current_thread() 函数先获取当前线程,而后再调用线程对象的 name 属性来获取线程的名字。
经过上面程序不难看出,启动线程的正确方法是调用 Thread 对象的 start() 方法,而不是直接调用 run() 方法,不然就变成单线程程序了。
须要指出的是,在调用线程对象的 run() 方法以后,该线程己经再也不处于新建状态,不要再次调用线程对象的 start() 方法。
注意,只能对处于新建状态的线程调用 start() 方法。也就是说,若是程序对同一个线程重复调用 start() 方法,将引起 RuntimeError 异常。
从上面四个小例子,咱们能够总结出:
start() 方法是启动一个子线程
run() 方法并不启动一个新线程,就是在主线程中调用了一个普通函数而已。
所以,若是你想启动多线程,就必须使用start()方法。
守护线程会在"该进程内全部非守护线程所有都运行完毕后,守护线程才会挂掉"。并非主线程运行完毕后守护线程挂掉。这一点是和守护进程的区别之处!
须要强调的是:运行完毕并不是终止运行**。
不管是进程仍是线程,都遵循:守护xxx会等待xxx运行完毕后被销毁
对主进程来讲,运行完毕指的是主进程代码运行完毕
对主线程来讲,运行完毕指的是主线程所在的进程内全部非守护线程通通运行完毕,主线程才算运行完毕
守护进程:主进程代码运行完毕,守护进程也就结束 (守护的是主进程)
主进程要等非守护进程都运行完毕后再回收子进程的资源(不然会产生僵尸进程)才结束
主进程等子进程是由于主进程要给子进程收尸(代用wait方法向操做系统发起回收资源信号(pid号,状态信息))
守护线程:非守护线程代码运行完毕,守护线程也就结束 (守护的是非守护线程)
主线程在其余非守护线程运行完毕后才算结束(主线程的结束意味着进程的结束,守护线程在此时就会被回收)
强调:主线程也是非守护线程(进程包含了线程)
总结:
主线程活着的时候,守护线程才会存活。主线程结束后,守护线程会自动被杀死结束运行。
主线程需等全部非守护线程退出后才会退出,若是想要结束非守护线程,咱们必须手动找出非守护线程将其杀死。
主线程启动两个子线程:
子线程0-守护线程,运行10秒退出
子线程1-非守护线程,运行1秒退出。
根据咱们上面的总结,咱们会知道:
主线程启动完子线程,等待全部非守护线程运行
非守护子线程1运行1秒退出
此时没有非守护线程运行,主线程退出
子线程0虽然任务还未完成,可是它是守护线程,会紧跟主线程退出。
例子
# 守护线程 from threading import Thread import time def func1(): while True: print("in func1") time.sleep(5) def func2(): print("in func2") time.sleep(1) t1 = Thread(target=func1,) t1.daemon = True t1.start() t2 = Thread(target=func2,) t2.start() print("主进程")
分析:这里的t1线程做为守护线程必定是执行不完的,由于其余非守护线程很快执行完了,主线程就要结束了,主线程结束进程要回收资源,因此t1做为守护线程立刻会被结束掉。
例子2
from threading import Thread import time def foo(): print(123) time.sleep(1) print("end123") def bar(): print(456) time.sleep(3) print("end456") t1=Thread(target=foo) t2=Thread(target=bar) t1.daemon=True t1.start() t2.start() print("主线程-------")
分析:虽然这里设置了t1是守护线程,可是因为t1线程运行的时间较短,因此这里的守护线程会完成运行,不会出现运行一半程序直接退出的状况。