DAY 35 GIL全局解释器锁、死锁、递归锁以及event事件与信号量、线程queue
2019-05-08 16:27
375 查看
原文链接:http://www.cnblogs.com/majingjie/p/10832686.html
一.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. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.) """
GIL是一个互斥锁:保护数据的安全(以牺牲运行效率来换取数据的安全)
阻止同一进程内多个线程同时执行(不能并行但是能够实现并发)
GIL全局解释器锁存在的原因是因为Cpython解释器的内存管理(垃圾处理机制)不是线程安全的
结论:在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势
1.那么python中多线程是否就没有作用了?
分情况考虑:
1.存在四个计算密集型的任务,每个任务耗时10S
单核情况下:
多线程好一点,消耗的资源少一点
多核情况下:
开四个进程:运行时间10S多一点
开四个线程:运行时间40S多一点
2.存在四个IO密集型的任务,每个任务IO 10S
单核情况下:
多线程好一点,多进程开启切换的时间较长
多核情况下:
多线程好一点,多进程开启切换的时间较长
总结:多线程和多进程都有自己的优点,要根据项目需求合理选择
测试结果:
1 # 计算密集型 2 from multiprocessing import Process 3 from threading import Thread 4 import os,time 5 def work(): 6 res=0 7 for i in range(100000000): 8 res*=i 9 10 11 if __name__ == '__main__': 12 l=[] 13 print(os.cpu_count()) # 本机为12核 14 start=time.time() 15 for i in range(12): 16 # p=Process(target=work) #耗时8s多 17 p=Thread(target=work) #耗时44s多 18 l.append(p) 19 p.start() 20 for p in l: 21 p.join() 22 stop=time.time() 23 print('run time is %s' %(stop-start)) 24 25 # IO密集型 26 from multiprocessing import Process 27 from threading import Thread 28 import threading 29 import os,time 30 def work(): 31 time.sleep(2) 32 33 34 if __name__ == '__main__': 35 l=[] 36 print(os.cpu_count()) #本机为12核 37 start=time.time() 38 for i in range(400): 39 p=Process(target=work) #耗时12s多,大部分时间耗费在创建进程上 40 # p=Thread(target=work) #耗时2s多 41 l.append(p) 42 p.start() 43 for p in l: 44 p.join() 45 stop=time.time() 46 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)
总结:对于不同的数据,想要保证安全,需要加不同的锁处理
GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程安全
保证的是在同一进程下多个线程之间的安全
三.死锁与递归锁
死锁现象:
from threading import Thread, Lock import time mutexA = Lock() mutexB = Lock() 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) mutexA.release() print('%s 释放了 A锁' % self.name) mutexB.release() print('%s 释放了 B锁' % self.name) def func2(self): mutexB.acquire() print('%s 抢到了 B锁' % self.name) time.sleep(0.1) mutexA.acquire() print('%s 抢到了 A锁' % self.name) mutexB.release() print('%s 释放了 B锁' % self.name) mutexA.release() print('%s 释放了 A锁' % self.name) for i in range(10): t = MyThread() t.start()
递归锁:
from threading import Thread, RLock import time # mutexA = Lock() # mutexB = Lock() start = time.time() mutexB = mutexA = 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) mutexA.release() print('%s 释放了 A锁' % self.name) mutexB.release() print('%s 释放了 B锁' % self.name) def func2(self): mutexB.acquire() print('%s 抢到了 B锁' % self.name) time.sleep(0.1) mutexA.acquire() print('%s 抢到了 A锁' % self.name) mutexB.release() print('%s 释放了 B锁' % self.name) mutexA.release() print('%s 释放了 A锁' % self.name) for i in range(10): t = MyThread() t.start()
总结:自定义锁一次acquire必须对应一次release,不能连续acquire
递归锁可以连续的acquire,每acquire一次计数+1,只针对第一次抢到的人
四.event事件
from threading import Thread, Event import time event = Event() def light(): print('红灯亮') time.sleep(5) event.set() # 给event发信号,解除阻塞 print('绿灯亮') def car(i): print('车%s is stop' % i) event.wait() # 阻塞 print('车%s is run' % i) l = Thread(target=light) l.start() for i in range(10): c = Thread(target=car,args=(i,)) c.start()
五.信号量
from threading import Thread, Semaphore import time import random sm = Semaphore(5) # 五个厕所五把锁
# 跟你普通的互斥锁区别在于,普通的互斥锁是独立卫生间,所有人抢一把锁
# 信号量 公共卫生间 有多个坑,所有人抢多把锁
def wait(name): with sm: print('%s is WCing' % name) time.sleep(random.randint(1,5)) for i in range(10): t = Thread(target=wait,args=(i,)) t.start()
五.信号量
import queue # 1.普通q # 2.先进后出q # 3.优先级q # 1.普通q q = queue.Queue(3) q.put(1) q.put(2) q.put(3) print(q.get()) print(q.get()) print(q.get()) # 先进后出q 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())
转载于:https://www.cnblogs.com/majingjie/p/10832686.html
相关文章推荐
- 简谈全局解释器锁,死锁与递归锁,信号量,Event事件
- Python3之多线程GIL、同步锁、信号量、死锁与递归锁、线程Queue、Event、定时器
- 并发编程 - 线程 - 1.互斥锁/2.GIL解释器锁/3.死锁与递归锁/4.信号量/5.Event事件/6.定时器
- 概述GIL(全局解释器锁) 以及 解决GIL带来的影响的第二种方法时python 子线程调用 C语言方法
- 线程、进程、daemon、GIL锁、线程锁、递归锁、信号量、计时器、事件、队列、多进程
- 6-[多线程]-互斥锁、GIL、死锁、递归锁、信号量
- python多线程,event,互斥锁,死锁,递归锁,信号量
- 互斥锁、死锁、递归锁、信号量、Event
- 关于js中return false、event.preventDefault()和event.stopPropagation()区别,以及阻止事件冒泡和阻止默认事件
- QT类之事件mousePressEvent以及mouseMoveEvent
- python基础-信号量Semaphore(进程_线程)、事件Event(进程_线程)
- 死锁与递归锁及信号量等
- 探究绑定事件的this指向以及event传参的小问题
- ajax全局事件引用方式以及各个事件(全局/局部)执行顺序
- 互斥锁、死锁现象、递归锁、信号量
- HTML文档对象的事件以及addEventListener与attachEvent介绍
- 关于原生事件绑定中attachEvent与addEventlistener中兼容性以及attachEvent函数中this指代window问题
- android事件传递机制以及onInterceptTouchEvent()和onTouchEvent()总结
- jquery之ajax——全局事件引用方式以及各个事件(全局/局部)执行顺序
- windows之全局键盘钩子以及键盘事件模拟触发