day32 并发编程之锁
并发编程之锁
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, time def work(): res = 0 for i in range(10000000): res *= i if __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): # 报错超过最大递归深度,这里改为400 p=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 time n = 100 def task(): global n tmp = n time.sleep(1) # 睡1秒,所有子进程都取到了100,阻塞1秒后再减1,结果为99 # 注销了time.sleep(1),GIL锁发挥作用,串行取值,100-100个子程序的1=0 n = tmp - 1 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
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 random sm = 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())