一 、GIL全局解释器
GIL是一个互斥锁:保证数据的安全(以牺牲效率来换取数据的安全)
阻止同一个进程内多个线程同时执行(不能并行但是能够实现并发)
并发:看起来像同时进行的
GIL全局解释器存在的原因是因为Cpython解释器的内存管理不是线程安全的
垃圾回收机制
1.引用计数
2.标记清除
3.分代回收
同一个进程下的多个线程不能实现并行但是能够实现并发,多个进程下的线程能够实现并行
问题:python多线程是不是就没有用了呢?
四个任务:计算密集的任务 每个任务耗时10s
单核情况下:
多线程好一点,消耗的资源少一点
多核情况下:
开四个进程:10s多一点
开四个线程:40s多一点
四个任务:IO密集的任务 每个任务io 10s
单核情况下:
多线程好一点
多核情况下:
多线程好一点
多线程和多进程都有自己的优点,要根据项目需求合理选择
# 计算密集型
from multiprocessing import Process from threading import Thread import os,time def work(): res=0 for i in range(100000000): res*=i if __name__ == '__main__': l=[] print(os.cpu_count()) # 本机为4核 start=time.time() for i in range(4): # p=Process(target=work) #耗时13.018355369567871 p=Thread(target=work) #耗时25.032227754592896 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 def work(): time.sleep(2) if __name__ == '__main__': l=[] print(os.cpu_count()) #本机为4核 start=time.time() for i in range(600): p=Process(target=work) #耗时4.699530839920044 # p=Thread(target=work) #耗时2.054128885269165 l.append(p) p.start() for p in l: p.join() stop=time.time() 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) -->> 0
对于不同的数据,要想保证安全,需要加不同的锁处理
GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程
保证的是同一个进程下多个线程之间的安全
三 、死锁与递归锁(了解)
代码演示
from threading import Thread,Lock,RLock import time """ 自定义锁一次acquire必须对应一次release,不能连续acquire 递归锁可以连续的acquire,每acquire一次计数加一:针对的是第一个抢到我的人 """ # # mutexA = Lock() # mutexB = Lock() mutexA = mutexB = 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) mutexB.release() print('%s 释放了B锁'%self.name) mutexA.release() print('%s 释放了A锁'%self.name) def func2(self): mutexB.acquire() print('%s 抢到了B锁'%self.name) time.sleep(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,5)) sm.release() if __name__ == '__main__': for i in range(20): t = Thread(target=task,args=('伞兵%s号'%i,)) t.start()
五 、 event事件(了解)
例子:
from threading import Event,Thread import time import random event = Event() def light(): print('红灯亮着!') time.sleep(3) event.set() # 解除阻塞,给我的event发了一个信号 print('绿灯亮了!') def car(i): print('%s 正在等红灯了'%i) event.wait() # 阻塞 print('%s 加油门飙车了'%i) t1 = Thread(target=light) t1.start() for i in range(10): t = Thread(target=car,args=(i,)) t.start()
六 、线程Queue(了解)
import queue # 1.普通q # 2.先进后出q # 3.优先级q # q=queue.Queue(3) # q.put(1) # q.put(2) # q.put(3) # print(q.get()) # print(q.get()) # print(q.get()) # 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())