并发编程之锁
1. GIL全局解释器锁
2. GIL与普通的互斥锁
3. 死锁
4. 信号量
5. event事件
6. 线程q
1. 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. """ """ ps:python解释器有很多种 最常见的就是Cpython解释器 GIL本质也是一把互斥锁:将并发变成串行牺牲效率保证数据的安全 用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行但是可以实现并发)python的多线程没法利用多核优势 是不是就是没有用了?GIL的存在是因为CPython解释器的内存管理不是线程安全的垃圾回收机制1.引用计数2.标记清除3.分代回收研究python的多线程是否有用需要分情况讨论 四个任务 计算密集型的 10s 单核情况下开线程更省资源 多核情况下开进程 10s开线程 40s四个任务 IO密集型的 单核情况下开线程更节省资源 多核情况下开线程更节省资源 """""" python的多线程到底有没有用 需要看情况而定 并且肯定是有用的多进程+多线程配合使用 """
计算密集型
# 计算密集型 from multiprocessing import Process from threading import Thread import os, timedef work():res = 0for i in range(10000000):res *= iif __name__ == '__main__':l = []print(os.cpu_count()) # 本机为8核start = time.time()for i in range(8):p = Process(target=work) # 耗时 0.7752041816711426# p = Thread(target=work) # 耗时 2.9953091144561768 l.append(p)p.start()for p in l:p.join()stop = time.time()print('run time is %s' % (stop - start))
I/O密集型
# 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()) #本机为8核start=time.time()for i in range(4000): # 报错超过最大递归深度,这里改为400p=Process(target=work) #耗时2.85s多,大部分时间耗费在创建进程上# p=Thread(target=work) #耗时2.02s多 l.append(p)p.start()for p in l:p.join()stop=time.time()print('run time is %s' %(stop-start))
2. GIL与普通的互斥锁
from threading import Thread import timen = 100def task():global ntmp = ntime.sleep(1) # 睡1秒,所有子进程都取到了100,阻塞1秒后再减1,结果为99# 注销了time.sleep(1),GIL锁发挥作用,串行取值,100-100个子程序的1=0n = tmp - 1t_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
3.死锁
from threading import Thread,Lock,current_thread,RLock import time """ Rlock可以被第一个抢到锁的人连续的acquire和release 每acquire一次锁身上的计数加1 每release一次锁身上的计数减1 只要锁的计数不为0 其他人都不能抢""" # mutexA = Lock() # mutexB = Lock() mutexA = mutexB = RLock() # A B现在是同一把锁class MyThread(Thread):def run(self): # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发 self.func1()self.func2()def func1(self):mutexA.acquire()print('%s抢到了A锁'%self.name) # self.name等价于current_thread().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(10):t = MyThread()t.start()class Demo(object): passobj1 = Demo()obj2 = Demo()print(id(obj1),id(obj2)) """ 只要类加括号实例化对象 无论传入的参数是否一样生成的对象肯定不一样 单例模式除外自己千万不要轻易的处理锁的问题 """
4 信号量
# 信号量可能在不同的领域中 对应不同的知识点 """ 互斥锁:一个厕所(一个坑位) 信号量:公共厕所(多个坑位) """ from threading import Semaphore,Thread import time import randomsm = Semaphore(5) # 造了一个含有五个的坑位的公共厕所def task(name):sm.acquire()print('%s占了一个坑位'%name)time.sleep(random.randint(1,3))sm.release()for i in range(40):t = Thread(target=task,args=(i,))t.start()
5 Event事件
from threading import Event,Thread import time# 先生成一个event对象 e = Event()def light():print('红灯正亮着')time.sleep(3)e.set() # 发信号print('绿灯亮了')def car(name):print('%s正在等红灯'%name)e.wait() # 等待信号print('%s加油门飙车了'%name)t = Thread(target=light) t.start()for i in range(10):t = Thread(target=car,args=('伞兵%s'%i,))t.start()
6 线程q
import queue """ 同一个进程下的多个线程本来就是数据共享 为什么还要用队列因为队列是管道+锁 使用队列你就不需要自己手动操作锁的问题 因为锁操作的不好极容易产生死锁现象 """# q = queue.Queue() # q.put('hahha') # print(q.get())# q = queue.LifoQueue() # q.put(1) # q.put(2) # q.put(3) # print(q.get())# q = queue.PriorityQueue() # # 数字越小 优先级越高 # q.put((10,'haha')) # q.put((100,'hehehe')) # q.put((0,'xxxx')) # q.put((-10,'yyyy')) # print(q.get())