第三十三天- 线程创建、join、守护线程、死锁
1.线程,线程创建
概念:在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程,线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程,车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线。流水线的工作需要电源,电源就相当于cpu。
所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。
多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。
创建:
线程创建方式一:
1 from multiprocessing import process 2 from threading import thread 3 4 5 def func(n): 6 print('xxxxx') 7 print('>>>',n) 8 9 10 if __name__ == '__main__': 11 12 # p = process(target=func,args=(1,)) 13 # p.start() 14 15 t = thread(target=func,args=(1,)) # 直接创建 16 t.start() 17 18 print('主线程结束')
面向对象创建:
1 from threading import thread 2 3 4 # 面向对象创建 5 class mythread(thread): # 继承thread父类 6 7 def __init__(self,xx): 8 super().__init__() 9 self.xx = xx 10 11 def run(self): # 必须有run,覆盖父类run中的pass 12 print(self.xx) 13 print('我重置父类方法') 14 15 def func1(self): # 写其他方法 16 print('我是func1') 17 18 19 if __name__ == '__main__': 20 21 t1 = mythread('xx') 22 t1.start() # 默认执行run 23 t1.func1() # 调用其他方法 24 # 25 26 from multiprocessing import process 27 from threading import thread
2.thread类方法
join方法:
主线程等待join子线程执行完毕后才执行
1 import time 2 from threading import thread 3 4 5 def func(n): 6 time.sleep(n) 7 print('我是子线程') 8 9 10 if __name__ == '__main__': 11 12 t = thread(target=func,args=(1,)) 13 t.start() 14 15 t.join() # 等待子线程执行结束 16 print('我是主线程,子线程结束再执行我')
其他方法:
1 '' 2 thread实例对象的方法 3 # isalive(): 返回线程是否活动的。 4 # getname(): 返回线程名。 5 # setname(): 设置线程名。 6 7 threading模块提供的一些方法: 8 # threading.currentthread(): 返回当前的线程变量。 9 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 10 # threading.activecount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果 11 '''
1 import time, threading 2 from threading import thread, current_thread 3 4 5 def func(): 6 time.sleep(2) 7 print('子线程,名字是:', current_thread().getname()) # 返回线程名 8 print('子线程,id是:', current_thread().ident) # 返回线程id 9 10 11 if __name__ == '__main__': 12 13 for i in range(10): 14 t = thread(target=func, ) 15 t.start() 16 17 print(threading.enumerate()) # 返回一个包含正在运行的list 18 print(threading.activecount()) # 返回正在运行的线程数量,等同len(threading.enumerate()) 19 20 print('主线程结束')
3.守护线程、事件
守护线程:
主进程结束,守护进程跟着结束,再执行非守护进程
主线程要等待所有非守护线程运行结束才会结束(因为他们属于同一进程)
需注意:运行结束并非终止运行
xxx.setdaemon(true) 或者 xxx.daemon = true
1 import time 2 from threading import thread 3 from multiprocessing import process 4 5 6 def func1(): 7 time.sleep(3) 8 print('任务1结束') 9 10 11 def func2(): 12 time.sleep(2) 13 print('任务2结束') 14 15 16 if __name__ == '__main__': 17 18 # p1 = process(target=func1,) 19 # p2 = process(target=func2,) 20 # # p1.daemon = true 21 # p2.daemon = true 22 # p1.start() 23 # p2.start() 24 # 25 # print('主进程结束') 26 27 t1 = thread(target=func1,) 28 t2 = thread(target=func2,) 29 # t1.setdaemon(true) # 只打印出 主线程和t2 因为t2时间比t1小 30 t2.setdaemon(true) # 会正常打印出所有 因为t1时间大于t2 31 t1.start() 32 t2.start() 33 34 print('主线程结束')
事件:
event.isset():返回event的状态值;
event.wait():如果 event.isset()==false将阻塞线程;
event.set(): 设置event的状态值为true,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
event.clear():恢复event的状态值为false。
1 import time 2 from threading import event 3 4 e = event() # 默认false状态 5 print(e.isset()) # 事件当前状态 6 7 e.set() # 改变成ture 8 print(e.isset()) 9 10 print('稍等...') 11 # e.clear() # 将e的状态改为false 12 e.wait() # 如果 event.isset()==false将阻塞线程 13 14 if e.isset() == true: 15 time.sleep(1) 16 print('滴滴滴,上车吧...')
4.线程间数据问题
开启一个线程所需要的时间要远小于开启一个进程
1 import time 2 from multiprocessing import process 3 from threading import thread 4 5 6 def func(i): 7 return '我是任务%s'%i 8 9 10 if __name__ == '__main__': 11 12 # 多线程 13 t_list = [] 14 t_start_time = time.time() 15 for i in range(10): 16 t = thread(target=func,args=(i,)) 17 t_list.append(t) 18 t.start() 19 20 [tt.join() for tt in t_list] 21 t_end_time = time.time() 22 t_dif_time = t_end_time - t_start_time 23 24 # 多进程 25 p_list = [] 26 p_start_time = time.time() 27 for i in range(10): 28 p = process(target=func,args=(i,)) 29 p_list.append(p) 30 p.start() 31 32 [pp.join() for pp in p_list] 33 p_end_time = time.time() 34 p_dif_time = p_end_time - p_start_time 35 # 结果受cpu影响 36 37 print('多线程耗时:',t_dif_time) 38 print('多进程耗时:',p_dif_time) 39 print('主线程结束') 40 41 ''' 42 多线程耗时: 0.0020008087158203125 43 多进程耗时: 0.4188823699951172 44 '''
线程之间共享进程资源(全局变量在多个线程之间共享),但也会导致数据不但全问题
1 import time 2 from threading import thread 3 4 num = 100 5 6 def func(): 7 global num 8 tep = num # tep替换 模拟多步操作 9 time.sleep(0.001) # 模拟延迟 10 tep -= 1 11 num = tep 12 13 14 if __name__ == '__main__': 15 16 t_list = [] 17 for i in range(100): 18 t = thread(target=func,) 19 t_list.append(t) 20 t.start() 21 22 [tt.join() for tt in t_list] 23 24 print('主线程的num',num) # 打印出不是100可知数据是共享的 25 26 # 理论上应该是0,但多线程是并发执行的,会出现上一个线程还在运算中时,下一个线程并未等待它返回值 27 # 也拿了原来的值进来运算,所以打印出了91,92,93不等,可知多线程数据是不安全的 28 ''' 29 主线程的num 92 30 '''
加锁解决多线程数据不安全问题
1 import time 2 from threading import thread,lock 3 4 num = 100 5 def func(lock,i): 6 global num 7 lock.acquire() # 加锁 8 9 tep = num 10 time.sleep(0.001) # 模拟延迟 11 tep -= 1 12 num = tep 13 14 lock.release() # 释放 15 16 17 if __name__ == '__main__': 18 t_list = [] 19 lock = lock() 20 for i in range(100): 21 t = thread(target=func,args=(lock,i)) 22 t_list.append(t) 23 t.start() 24 25 [tt.join() for tt in t_list] 26 print('主线程num',num) 27 28 ''' 29 主线程num 0 30 '''
信号量semaphore
1 import time,random 2 from threading import thread,semaphore 3 4 5 def func(i,s): 6 s.acquire() 7 print('%s张烧饼'%i) 8 time.sleep(random.randint(1,3)) 9 s.release() 10 # 出来一个进去一个 始终6个 最后不足6个就都进来了 11 12 13 if __name__ == '__main__': 14 s = semaphore(6) # 与lock类似,不过可限制最大连接数,如这里同时只有6个线程可以获得semaphore 15 for i in range(28): 16 t = thread(target=func,args=(i,s,)) 17 t.start()
5.死锁和递归锁
死锁现象:有多个锁时,双方互相等待对方释放对方手里拿到的那个锁导致死锁
1 import time 2 from threading import thread,lock 3 4 5 def func1(lock_a,lock_b): 6 lock_a.acquire() 7 print('张全蛋拿到了a锁') 8 time.sleep(0.5) 9 lock_b.acquire() 10 print('张全蛋拿到了b锁') 11 lock_b.release() 12 lock_a.release() 13 14 15 def func2(lock_a,lock_b): 16 lock_b.acquire() 17 print('赵二狗拿到了b锁') 18 lock_a.acquire() 19 print('赵二狗拿到了a锁') 20 lock_a.release() 21 lock_b.release() 22 23 24 if __name__ == '__main__': 25 26 lock_a = lock() 27 lock_b = lock() 28 t1 = thread(target=func1,args=(lock_a,lock_b,)) 29 t2 = thread(target=func2,args=(lock_a,lock_b,)) 30 t1.start() 31 t2.start()
递归锁:rlock
rlock管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
1 # 递归锁解决死锁现象 2 import time 3 from threading import thread,lock,rlock 4 5 6 def func1(lock_a,lock_b): 7 lock_a.acquire() 8 print('张全蛋拿到了a锁') 9 time.sleep(0.5) 10 lock_b.acquire() 11 print('张全蛋拿到了b锁') 12 lock_b.release() 13 lock_a.release() 14 15 16 def func2(lock_a,lock_b): 17 lock_b.acquire() 18 print('赵二狗拿到了b锁') 19 lock_a.acquire() 20 print('赵二狗拿到了a锁') 21 lock_a.release() 22 lock_b.release() 23 24 25 if __name__ == '__main__': 26 27 # lock_a = lock() 28 # lock_b = lock() 29 lock_a = lock_b = rlock() 30 t1 = thread(target=func1,args=(lock_a,lock_b,)) 31 t2 = thread(target=func2,args=(lock_a,lock_b,)) 32 t1.start() 33 t2.start()