在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势
结论:在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势
1、存在原因:
GIL是一个互斥锁:保证数据的安全(以牺牲效率来换取数据的安全)
阻止同一个进程内多个线程同时执行(不能并行但能并发)
GIL全局解释器存在的原因是Cpython解释器的内存管理不是线程安全的
同一个进程下的多个线程不能实现并行但是能够实现并发,多个进程下的线程能够实现并行
2、多线程和多进程都有自己的优点,要根据项目需求合理选择
四个任务:计算密集的任务 每个任务耗时10s
单核情况下:
多线程好一点,消耗的资源少一点
多核情况下:
开四个进程:10s多一点
开四个线程:40s多一点
四个任务:IO密集的任务 每个任务io 10s
单核情况下:
多线程好一点
多核情况下:
多线程好一点
二、GIL全局解释器锁与普通锁的互斥锁的区别
对于不同的数据,要想保证数据的安全,需要加不同的锁处理
GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程
保证的是同一个进行下多个线程之间的安全
三、死锁与递归锁
自定义锁一次acquire必须对应一次release,不能连续acquire
递归锁可以连续acquire,每acquire一次计数加一(针对的是第一个抢到的)
1、死锁
复制代码
from threading import Thread,Lock
import time
mutexA = Lock()
mutexB = Lock()
class MyThread(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) # B锁在1手里,拿不到,阻塞
mutexB.release()
print('%s 释放了B锁' % self.name)
mutexA.release()
print('%s 释放了A锁' % self.name)
def f2(self):
mutexB.acquire()
print('%s 抢到B锁了' % self.name) # 1抢到了B锁,这时2抢到了A锁
time.sleep(1)
mutexA.acquire()
print('%s 抢到A锁了' % self.name) # A锁在2手里,拿不到,阻塞
mutexA.release()
print('%s 释放了A锁' % self.name)
mutexB.release()
print('%s 释放了B锁' % self.name)
for i in range(100):
t = MyThread()
t.start()
复制代码
2、递归锁
复制代码
from threading import Thread,RLock
import time
mutexA = mutexB = RLock()
class MyThread(Thread):
def run(self):
self.f1()
self.f2()
def f1(self):
mutexA.acquire()
print('%s 抢到A锁了' % self.name) # 计数加1
mutexB.acquire()
print('%s 抢到B锁了' % self.name) # 计数加1,变为2
mutexB.release()
print('%s 释放了B锁' % self.name) # 计数减一
mutexA.release()
print('%s 释放了A锁' % self.name) # 计数变为0
def f2(self):
mutexB.acquire()
print('%s 抢到B锁了' % self.name) # 计数又变1
time.sleep(1) # 所有线程都起来,但是发现计数为1,等待在原地
mutexA.acquire()
print('%s 抢到A锁了' % self.name)
mutexA.release()
print('%s 释放了A锁' % self.name)
mutexB.release()
print('%s 释放了B锁' % self.name)
for i in range(100):
t = MyThread()
t.start()
复制代码
四、信号量
信号量就是规定了有多少把钥匙,所有人抢多把锁
复制代码
from threading import Thread,Semaphore
import time
import random
sm = Semaphore(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):
t = Thread(target=task,args=(i,))
t.start()
复制代码
五、event事件
一些线程需要等待另外一些线程运行完毕才能运行,类似于发射信号一样
复制代码
from threading import Event,Thread
import time
event = Event()
def light():
print('红灯正亮着')
time.sleep(3)
event.set() # 给event发了一个信号,解除阻塞
print('绿灯亮了')
def car(i):
print('%s 正在等红灯'%i)
event.wait() # 阻塞,在等event的信号
print('%s起飞了'%i)
t1 = Thread(target=light)
t1.start()
for i in range(10):
t = Thread(target=car,args=(i,))
t.start()
复制代码
六、线程queue:导入模块queue
1、普通q:正常放取,满了再放会阻塞在原地,一个都取不到,空了再取会阻塞在原地
复制代码
q = queue.Queue(3)
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())
复制代码
2、先进后出:每次取都是取的队列中最后一个,没有再取会阻塞在原地
复制代码
q = queue.LifoQueue(5)
q.put(1)
q.put(2)
q.put(3)
q.put(4)
q.put(5)
print(q.get())
复制代码
3、优先级:优先取出最小的那个数
复制代码
q = queue.PriorityQueue()
q.put((10,'a'))
q.put((-1,'a'))
q.put((100,'a'))
print(q.get())
print(q.get())
print(q.get())
复制代码
因篇幅问题不能全部显示,请点此查看更多更全内容