python并发编程-GIL全局解释锁,Event事件,信号量
一、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解释器,
1.GLL本质也是一把互斥锁:将并发变成串行牺牲效率保证数据的安全,用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行但是可以实现并发)
GLL的存在是因为CPython解释器的内存管理不是线程安全的、
2.研究python的多线程是否有需要分情况讨论
cpu/时间/任务 | 四个任务 计算机密集型 10s | 四个任务 IO密集型 |
单核情况下 | 开线程更省资源 | 开线程更节省资源 |
多核情况下 | 开进程:10s 开线程:40s |
开线程更节省资源 |
# 计算密集型 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()) # 本机为6核 start=time.time() for i in range(6): # p=Process(target=work) #耗时 4.732933044433594 p=Thread(target=work) #耗时 22.83087730407715 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()) #本机为6核 start=time.time() for i in range(4000): p=Process(target=work) #耗时9.001083612442017s多,大部分时间耗费在创建进程上 # p=Thread(target=work) #耗时2.051966667175293s多 l.append(p) p.start() for p in l: p.join() stop=time.time() print('run time is %s' %(stop-start))
ps:python的多线程到底有没有用,需要看情况而定,并且肯定是有用的
多进程+多线程配合使用
二、死锁现象
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()
用Rlock模块解决问题,定义同一把锁(递归锁)
Rlock可以被第一个抢到锁的人连续的acquire和release
每acquire一次锁身上的计数加1
每release一次锁身上的计数减1
只要锁的计数不为0 其他人都不能抢
三、信号量(互斥锁类似,每轮提供多个位置)
# 信号量可能在不同的领域中 对应不同的知识点 """ 互斥锁:一个厕所(一个坑位) 信号量:公共厕所(多个坑位) """ 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()
四、event事件
一个线程等待另一个线程执行信号
even.set() #发送信号
even.wait() # 等待信号
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()
五、线程queue问题
同一个进程下的多个线程本来就是数据共享,为什么还要用队列,
因为队列是管道+锁,使用队列就不需要自己手动操作锁的问题。
因为锁操作的不好极容易产生死锁现象
直接 import queue 调用,和进程调用队列有点不同
方法:
1.queue.Queue() # 队列
2.queue.LifoQueue() # 堆栈
3.queue.PriorityQueue() # 数字越小,优先级越高
只有一个参数,元组形式
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())