一.GIL全局解释器锁
""" In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.) """
GIL是一个互斥锁:保护数据的安全(以牺牲运行效率来换取数据的安全)
阻止同一进程内多个线程同时执行(不能并行但是能够实现并发)
GIL全局解释器锁存在的原因是因为Cpython解释器的内存管理(垃圾处理机制)不是线程安全的
结论:在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势
1.那么python中多线程是否就没有作用了?
分情况考虑:
1.存在四个计算密集型的任务,每个任务耗时10S
单核情况下:
多线程好一点,消耗的资源少一点
多核情况下:
开四个进程:运行时间10S多一点
开四个线程:运行时间40S多一点
2.存在四个IO密集型的任务,每个任务IO 10S
单核情况下:
多线程好一点,多进程开启切换的时间较长
多核情况下:
多线程好一点,多进程开启切换的时间较长
总结:多线程和多进程都有自己的优点,要根据项目需求合理选择
测试结果:

1 # 计算密集型 2 from multiprocessing import Process 3 from threading import Thread 4 import os,time 5 def work(): 6 res=0 7 for i in range(100000000): 8 res*=i 9 10 11 if __name__ == '__main__': 12 l=[] 13 print(os.cpu_count()) # 本机为12核 14 start=time.time() 15 for i in range(12): 16 # p=Process(target=work) #耗时8s多 17 p=Thread(target=work) #耗时44s多 18 l.append(p) 19 p.start() 20 for p in l: 21 p.join() 22 stop=time.time() 23 print('run time is %s' %(stop-start)) 24 25 # IO密集型 26 from multiprocessing import Process 27 from threading import Thread 28 import threading 29 import os,time 30 def work(): 31 time.sleep(2) 32 33 34 if __name__ == '__main__': 35 l=[] 36 print(os.cpu_count()) #本机为12核 37 start=time.time() 38 for i in range(400): 39 p=Process(target=work) #耗时12s多,大部分时间耗费在创建进程上 40 # p=Thread(target=work) #耗时2s多 41 l.append(p) 42 p.start() 43 for p in l: 44 p.join() 45 stop=time.time() 46 print('run time is %s' %(stop-start))
二.GIL与普通锁对比
from threading import Thread,Lock import time mutex = Lock() n = 100 def task(): global n mutex.acquire() tmp = n time.sleep(0.1) n = tmp - 1 mutex.release() t_list = [] for i in range(100): t = Thread(target=task) t.start() t_list.append(t) for t in t_list: t.join() print(n)
总结:对于不同的数据,想要保证安全,需要加不同的锁处理
GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程安全
保证的是在同一进程下多个线程之间的安全
三.死锁与递归锁
死锁现象:
from threading import Thread, Lock import time mutexA = Lock() mutexB = Lock() class MyThread(Thread): def run(self): self.func1() self.func2() def func1(self): mutexA.acquire() print('%s 抢到了 A锁' % self.name) mutexB.acquire() print('%s 抢到了 B锁' % self.name) mutexA.release() print('%s 释放了 A锁' % self.name) mutexB.release() print('%s 释放了 B锁' % self.name) def func2(self): mutexB.acquire() print('%s 抢到了 B锁' % self.name) time.sleep(0.1) mutexA.acquire() print('%s 抢到了 A锁' % self.name) mutexB.release() print('%s 释放了 B锁' % self.name) mutexA.release() print('%s 释放了 A锁' % self.name) for i in range(10): t = MyThread() t.start()
递归锁:
from threading import Thread, RLock import time # mutexA = Lock() # mutexB = Lock() start = time.time() mutexB = mutexA = RLock() class MyThread(Thread): def run(self): self.func1() self.func2() def func1(self): mutexA.acquire() print('%s 抢到了 A锁' % self.name) mutexB.acquire() print('%s 抢到了 B锁' % self.name) mutexA.release() print('%s 释放了 A锁' % self.name) mutexB.release() print('%s 释放了 B锁' % self.name) def func2(self): mutexB.acquire() print('%s 抢到了 B锁' % self.name) time.sleep(0.1) mutexA.acquire() print('%s 抢到了 A锁' % self.name) mutexB.release() print('%s 释放了 B锁' % self.name) mutexA.release() print('%s 释放了 A锁' % self.name) for i in range(10): t = MyThread() t.start()
总结:自定义锁一次acquire必须对应一次release,不能连续acquire
递归锁可以连续的acquire,每acquire一次计数+1,只针对第一次抢到的人
四.event事件
from threading import Thread, Event import time event = Event() def light(): print('红灯亮') time.sleep(5) event.set() # 给event发信号,解除阻塞 print('绿灯亮') def car(i): print('车%s is stop' % i) event.wait() # 阻塞 print('车%s is run' % i) l = Thread(target=light) l.start() for i in range(10): c = Thread(target=car,args=(i,)) c.start()
五.信号量
from threading import Thread, Semaphore import time import random sm = Semaphore(5) # 五个厕所五把锁
# 跟你普通的互斥锁区别在于,普通的互斥锁是独立卫生间,所有人抢一把锁
# 信号量 公共卫生间 有多个坑,所有人抢多把锁
def wait(name): with sm: print('%s is WCing' % name) time.sleep(random.randint(1,5)) for i in range(10): t = Thread(target=wait,args=(i,)) t.start()
五.信号量
import queue # 1.普通q # 2.先进后出q # 3.优先级q # 1.普通q q = queue.Queue(3) q.put(1) q.put(2) q.put(3) print(q.get()) print(q.get()) print(q.get()) # 先进后出q q = queue.LifoQueue(5) q.put(1) q.put(2) q.put(3) q.put(4) print(q.get()) # 优先级q q = queue.PriorityQueue() q.put((10, 'a')) q.put((-1, 'b')) q.put((100, 'c')) print(q.get()) print(q.get()) print(q.get())