第三十三天- 线程创建、join、守护线程、死锁
1.线程,线程创建
概念:在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程,线程顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程,车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一个流水线。流水线的工作需要电源,电源就相当于cpu。
所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。
多线程(即多个控制线程)的概念是,在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。
创建:
线程创建方式一:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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('主线程结束')
面向对象创建:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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子线程执行完毕后才执行
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 '''
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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.线程间数据问题
开启一个线程所需要的时间要远小于开启一个进程
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 '''
线程之间共享进程资源(全局变量在多个线程之间共享),但也会导致数据不但全问题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 '''
加锁解决多线程数据不安全问题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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.死锁和递归锁
死锁现象:有多个锁时,双方互相等待对方释放对方手里拿到的那个锁导致死锁
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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()。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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()