一,GIL全局解释器锁
1.1GIL也是互斥锁的一种,相当于执行权限,每个进程都会存在一把GIL锁,同一进程下的多个线程必须抢到GIL之后才能使cpython解释器来执行自己的代码,即同一进程下的多线程无法实现并行,但是能实现并发,它主要调节了多线程对解释器的竞争变得有序
1.2为何要有GIL
因为Cpython解释器的垃圾回收机制不是线程安全的
补充知识点:
计算密集型:应该使用多进程
ltiprocessing import Process from threading import Thread import os,time #纯计算型 def work(): res = 0 for i in range(100000): res *=i if __name__ == '__main__': l = [] print(os.cpu_count()) #电脑中cpu的个数 start = time.time() for i in range(6): p = Process(target=work) #run time is 0.17092013359069824 # p = Thread(target=work) #run time is 0.024586200714111328 l.append(p) p.start() for p in l: p.join() stop = time.time() print('run time is %s' %(stop-start))
IO密集型:应该开启多线程
from multiprocessing import Process from threading import Thread import threading import os,time #睡眠是纯IO型 def work(): time.sleep(2) if __name__ == '__main__': l = [] start = time.time() for i in range(20): p = Process(target=work) #run time is 2.40376615524292 # p = Thread(target=work) #run time is 2.0061230659484863 l.append(p) p.start() for p in l: p.join() stop = time.time() print('run time is %s' %(stop-start))
二,自定义锁与GIL锁
有个GIL锁只能控制多个线程并发执行的顺序,并不能解决全局变量中的多线程竞争的很乱问题,这是就必须使用自定义锁
from threading import Thread,Lock import time mutex = Lock() n = 100 def task(): global n with mutex: #自定义锁 temp = n time.sleep(0.1) n = temp-1 if __name__ == '__main__': l=[] for i in range(100): t = Thread(target=task) l.append(t) t.start() for t in l: t.join() print(n)
最后的输出结果是0
二,死锁与递归锁
死锁现象:一个进程里有两把锁出现多个线程开始竞争这两把锁,每个线程里面定义两个函数,一个先抢锁A,然后再抢锁B,另一个函数先抢锁B然后再抢锁A,这样一个线程正好抢到锁A想要去抢锁B,另一个线程正好抢到锁B,想要去抢锁A.这两个线程互相拥有各自想要得到的锁,都阻塞到了抢另一把锁的代码中,使整个程序不能继续往下运行,就出现了死锁现象.(解决方法:递归锁)
递归锁:说的简单一点就是把几把锁合并成一把锁,每个线程都在抢这一把锁,只要抢到了,里面的数字就由原来的0改成1,这个线程只要抢到这把锁,就可以给它上好几次锁,也就是把里面的数字增加,其他线程只要看到锁里面的数字不是0就会等着,直到抢到这把锁的线程把数字都释放为0时,其他线程才能继续抢锁.(特点:可以连续acquire)
from threading import Thread,Lock,RLock import time #mutexA = Lock() #mutexB = Lock() 这样是不能解决问题,反而出现死锁现象 mutexA=mutexB=RLock() #递归锁可以无限递归下去 class Mythead(Thread): def run(self): self.f1() self.f2() def f1(self): mutexA.acquire() print('%s抢到A锁' %self.name) mutexB.acquire() print('%s抢到B锁' %self.name) mutexB.release() mutexA.release() def f2(self): mutexB.acquire() print('%s抢到了B锁' %self.name) time.sleep(2) mutexA.acquire() print('%s抢到了A锁' %self.name) mutexA.release() mutexB.release() if __name__ == '__main__': for i in range(10): t = Mythead() t.start()
三,信号量
导入一个Semaphore方法,限制并行的线程个数(区别于互斥锁,将多个编程编程串行),将多个并行的线程,控制为几个并行,如果超过了数量,没有抢到的就会等待,直到有的线程执行完毕.
from threading import Thread,Semaphore import time,random sm = Semaphore(5) #控制并行的线程个数为5 def task(name): sm.acquire() print('%s正在执行' %name) time.sleep(random.randint(1,3)) sm.release() if __name__ == '__main__': for i in range(20): #同时产生了20个线程 t = Thread(target=task) t.start()
四,Event事件
一个进程中存在两种不同的线程,也就是定义两个函数(或者类),一种线程满足什么条件时在Event()容器里放一个可以通行的信号True( event.set()),另一类线程在没有接受到可以通行的信号的时候都等待在(event.wait())这里,当接受到可以通行的信号的时候会并行通过
event.isSet():返回event的状态值;
event.wait():如果 event.isSet()==False将阻塞线程
event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统 调度
event.clear():恢复event的状态值为False
from threading import Thread,Event import time event = Event() #相当于存放通过信号的容器 def light(): print('红灯亮着') time.sleep(3) event.set() #绿灯亮 def car(name): print('车%s正在等绿灯' %name) event.wait() #等绿灯 print('车%s通行' %name) if __name__ == '__main__': #红绿灯 t1= Thread(target=light) t1.start() #车 for i in range(3): t = Thread(target=car,args = (i,)) t.start()
执行结果如下:
红灯亮着 车0正在等绿灯 车1正在等绿灯 车2正在等绿灯 车2通行 车0通行 车1通行
五,线程queue
存在三种形式,(1)先进先出
import queue queue.Queue() #先进先出 q=queue.Queue(3) q.put(1) q.put(2) q.put(3) print(q.get()) print(q.get()) print(q.get())
执行的结果如下:
1 2 3
(2)先进后出
import queue queue.LifoQueue() #后进先出->堆栈 q=queue.LifoQueue(3) q.put(1) q.put(2) q.put(3) print(q.get()) print(q.get()) print(q.get())
执行结果如下
3 2 1
(3)按优先级取值
import queue queue.PriorityQueue() #按优先级(优先级用数字表示,数字越小表示的优先级越高) q = queue.PriorityQueue(3) q.put((10,'a')) q.put((-1,'b')) q.put((100,'c')) print(q.get()) print(q.get()) print(q.get())
执行结果如下:
b a c