1.互斥锁: 原理:将并行变成串行 精髓:局部串行,只针对共享数据修改 保护不同的数据就应该用不用的锁
1 from threading import Thread, Lock 2 import time 3 4 n = 100 5 6 def task(): 7 global n 8 mutex.acquire() # 效率低了 但是数据安全了 9 temp = n10 time.sleep(0.1) # 100个线程 都拿到了100 所以就是 100个线程100-111 n = temp - 112 mutex.release()13 14 15 if __name__ == '__main__':16 mutex = Lock()17 t_l = []18 for i in range(100):19 t = Thread(target=task)20 t_l.append(t)21 t.start()22 23 for t in t_l:24 t.join()25 26 print('主', n)27 """28 主 99 原因: 100个线程 都拿到了100 所以就是 100个线程100-1 数据不安全 效率高但是不安全29 要将并行改为串行 30 """31 """32 主 0 原因:效率低了 但是数据安全了33 """
2.GIL: global interpreter lock python3 test.py ps aux | grep test # linux tasklist | findstr python # windows python.exe 运行python 会有几步: 1.会有一个进程,进程内存空间 python解释器的代码先加载到内存空间 2.test.py 内容加载到内存 3.解释执行;代码交给了python解释器 线程干活指向了python代码 python代码当作参数传给了解释器 线程拿到解释器的代码,拿着python代码当作参数,执行 垃圾回收线程运行解释器的代码 垃圾回收线程和某一个线程冲突了,数据不安全,
开多个进程,GIL就没影响了, cpython解释器垃圾回收线程定期启动一个 GIL:互斥锁,保证数据的安全 对CPython解释器,同一时间只有一个线程运行 GIL.acquire() 这样垃圾线程和线程就不会冲突了,这样回收机制就变得安全了 GIL.release() python解释器,多线程有GIL存在,保证了一个进程下面多个线程的执行是一个一个执行的 有GIL与自动的锁的工作原理:
总结: 1.GIL 一个进程内的多个线程同一时间只能运行一个线程,垃圾回收线程是安全的 2.针对不同的数据,就应该加不同的锁,解释器级别的GIL锁,只能保护解释器级别的数据, 不能保护自己的数据,针对自己的共享数据还要加锁; 线程首先抢的是;GIL锁,之后才是mutex 官网: 结论:在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势 GIL的存在:同一时刻,只能有一个线程在运行 多核,多进程,但进程开销大,多线程,又不能用多核 ? cpu干计算的,多个cpu 1.如果是干计算的操作,多核省时间 2.如果干IO阻塞型操作,多核没用 程序运行:都会干计算和IO操作 四个任务: 1.1个核:开多线程 ,因为多进程能用上多核 2.多核: 计算密集型:用多进程,用多核,eg:金融行业的,计算比较多,虽然多进程开销大,但多核,保证了计算快 IO密集型:用多线程,同一时间只能用一个核,1个核一个进程,多线程就在一个核上来回切和四个核来回切是一样的 现在写的软件: 网络打交道,网络的IO IO密集型,用多线程
1 """ 2 计算密集型应该用: 多进程 效率高 3 """ 4 from multiprocessing import Process 5 from threading import Thread 6 import os,time 7 8 def work(): 9 res=010 for i in range(100000000):11 res*=i12 13 14 if __name__ == '__main__':15 l=[]16 print(os.cpu_count()) #本机为8核17 start=time.time()18 for i in range(8):19 # p=Process(target=work) #耗时8s多20 p=Thread(target=work) #耗时37s多21 l.append(p)22 p.start()23 for p in l:24 p.join()25 stop=time.time()26 print('run time is %s' %(stop-start))27 28 """29 IO密集型:多线程 效率高30 """31 from multiprocessing import Process32 from threading import Thread33 import threading34 import os,time35 def work():36 time.sleep(2)37 print('===>')38 39 if __name__ == '__main__':40 l=[]41 print(os.cpu_count()) #本机为8核42 start=time.time()43 for i in range(400):44 # p=Process(target=work) #耗时8s多,大部分时间耗费在创建进程上45 p=Thread(target=work) #耗时2s多46 l.append(p)47 p.start()48 for p in l:49 p.join()50 stop=time.time()51 print('run time is %s' %(stop-start))
3.死锁: 你拿着我的锁,我拿着你的锁 互斥锁:Lock() 互斥锁只能acquire一次 递归锁:RLock() 可以连续acquire多次,每acquire一次计数器+1, 只有计数为0时,才能被抢到acquire
1 from threading import Thread,Lock 2 import time 3 4 mutexA=Lock() 5 mutexB=Lock() 6 7 class MyThread(Thread): 8 def run(self): 9 self.f1()10 self.f2()11 12 def f1(self):13 mutexA.acquire()14 print('%s 拿到了A锁' %self.name)15 16 mutexB.acquire()17 print('%s 拿到了B锁' %self.name)18 mutexB.release()19 20 mutexA.release()21 22 23 def f2(self):24 mutexB.acquire()25 print('%s 拿到了B锁' % self.name)26 time.sleep(0.1)27 28 mutexA.acquire()29 print('%s 拿到了A锁' % self.name)30 mutexA.release()31 32 mutexB.release()33 34 if __name__ == '__main__':35 for i in range(10):36 t=MyThread()37 t.start()38 """39 Thread-1 拿到了A锁 # 死锁了 卡住了40 Thread-1 拿到了B锁41 Thread-1 拿到了B锁42 Thread-2 拿到了A锁43 """
1 # 互斥锁只能acquire一次 2 # from threading import Thread,Lock 3 # 4 # mutexA=Lock() 5 # 6 # mutexA.acquire() 7 # mutexA.release() 8 9 # 递归锁:可以连续acquire多次,每acquire一次计数器+1,只有计数为0时,才能被抢到acquire10 from threading import Thread,RLock11 import time12 13 mutexB=mutexA=RLock()14 15 class MyThread(Thread):16 def run(self):17 self.f1()18 self.f2()19 20 def f1(self):21 mutexA.acquire()22 print('%s 拿到了A锁' %self.name)23 24 mutexB.acquire()25 print('%s 拿到了B锁' %self.name)26 mutexB.release()27 28 mutexA.release()29 30 31 def f2(self):32 mutexB.acquire()33 print('%s 拿到了B锁' % self.name)34 time.sleep(2)35 36 mutexA.acquire()37 print('%s 拿到了A锁' % self.name)38 mutexA.release()39 40 mutexB.release()41 42 if __name__ == '__main__':43 for i in range(10):44 t=MyThread()45 t.start()46 """47 Thread-1 拿到了A锁 # 解决了 死锁48 Thread-1 拿到了B锁49 Thread-1 拿到了B锁50 Thread-1 拿到了A锁51 Thread-2 拿到了A锁52 Thread-2 拿到了B锁53 Thread-2 拿到了B锁54 Thread-2 拿到了A锁55 Thread-4 拿到了A锁56 Thread-4 拿到了B锁57 Thread-5 拿到了A锁58 Thread-5 拿到了B锁59 Thread-5 拿到了B锁60 Thread-5 拿到了A锁61 Thread-7 拿到了A锁62 Thread-7 拿到了B锁63 Thread-7 拿到了B锁64 Thread-7 拿到了A锁65 Thread-9 拿到了A锁66 Thread-9 拿到了B锁67 Thread-9 拿到了B锁68 Thread-9 拿到了A锁69 Thread-3 拿到了A锁70 Thread-3 拿到了B锁71 Thread-3 拿到了B锁72 Thread-3 拿到了A锁73 Thread-6 拿到了A锁74 Thread-6 拿到了B锁75 Thread-6 拿到了B锁76 Thread-6 拿到了A锁77 Thread-10 拿到了A锁78 Thread-10 拿到了B锁79 Thread-10 拿到了B锁80 Thread-10 拿到了A锁81 Thread-8 拿到了A锁82 Thread-8 拿到了B锁83 Thread-8 拿到了B锁84 Thread-8 拿到了A锁85 Thread-4 拿到了B锁86 Thread-4 拿到了A锁87 """
4.信号量 信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行, 信号量同一时间可以有5个任务拿到锁去执行 信号量:同一时间有多个线程在进行
1 from threading import Thread,Semaphore,currentThread 2 import time,random 3 4 sm=Semaphore(1) 5 6 def task(): 7 # sm.acquire() 8 # print('%s in' %currentThread().getName()) 9 # sm.release()10 with sm: # 类似于sm.acquire() # 同一时间可以来3个人,1个人,或者2个人11 print('%s in' %currentThread().getName())12 time.sleep(random.randint(1,3))13 14 15 if __name__ == '__main__':16 for i in range(10):17 t=Thread(target=task)18 t.start()19 """20 Thread-1 in21 Thread-2 in22 Thread-3 in23 24 Thread-4 in25 26 27 Thread-6 in28 Thread-5 in29 Thread-7 in30 31 32 Thread-8 in33 Thread-9 in34 35 Thread-10 in 36 """
5.Event: 多个线程之间同步的,一个线程告诉另一些线程可以做其他的活了 event.wait() event.wait(2) event.set() event.is_set() event.clear()
1 from threading import Thread,Event 2 import time 3 4 event=Event() 5 # event.wait() # 等 ...直到 set 6 # event.set() 7 8 9 def student(name):10 print('学生%s 正在听课' %name)11 # event.wait() # 学生要等7秒 才能下课12 event.wait(2) # 学生等2秒 直接下课了13 14 print('学生%s 课间活动' %name)15 16 17 def teacher(name):18 print('老师%s 正在授课' %name)19 time.sleep(7)20 event.set()21 22 23 if __name__ == '__main__':24 stu1=Thread(target=student,args=('alex',))25 stu2=Thread(target=student,args=('wxx',))26 stu3=Thread(target=student,args=('yxx',))27 t1=Thread(target=teacher,args=('egon',))28 29 stu1.start()30 stu2.start()31 stu3.start()32 t1.start()33 34 35 # ------------------36 # 设置链接的超时时间37 from threading import Thread,Event,currentThread38 import time39 40 event=Event()41 42 def conn():43 # print('%s is connecting'%currentThread().getName())44 # event.wait()45 # print('%s is connected'%currentThread().getName())46 47 n=048 while not event.is_set():49 if n == 3:50 print('%s try too many times' %currentThread().getName())51 return52 print('%s try %s' %(currentThread().getName(),n))53 event.wait(0.5)54 n+=155 56 print('%s is connected' %currentThread().getName())57 58 59 def check():60 print('%s is checking' %currentThread().getName())61 time.sleep(5)62 event.set()63 64 65 if __name__ == '__main__':66 for i in range(3):67 t=Thread(target=conn)68 t.start()69 t=Thread(target=check)70 t.start()71 """72 Thread-1 try 073 Thread-2 try 074 Thread-3 try 075 Thread-4 is checking76 Thread-3 try 177 Thread-2 try 178 Thread-1 try 179 Thread-3 try 280 Thread-1 try 281 Thread-2 try 282 Thread-3 try too many times83 Thread-2 try too many times84 Thread-1 try too many times85 """
6.定时器:Timer t=Timer(5,task,args=('egon',)) t.start() t.cancel()
1 from threading import Timer 2 3 def task(name): 4 print('hello %s' %name) 5 6 t=Timer(5,task,args=('egon',)) # 就是起了一个线程 7 t.start() 8 9 # ----------------------10 from threading import Timer11 import random12 13 class Code:14 def __init__(self):15 self.make_cache()16 17 def make_cache(self,interval=10):18 self.cache=self.make_code()19 print(self.cache)20 self.t=Timer(interval,self.make_cache)21 self.t.start()22 23 def make_code(self,n=4):24 res=''25 for i in range(n):26 s1=str(random.randint(0,9))27 s2=chr(random.randint(65,90))28 res+=random.choice([s1,s2])29 return res30 31 def check(self):32 while True:33 code=input('请输入你的验证码>>: ').strip()34 if code.upper() == self.cache:35 print('验证码输入正确')36 self.t.cancel()37 break38 39 obj=Code()40 obj.check()