全局解释锁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.)
- '''
结论:在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势
前言:
- - GIL其实就是一把互斥锁(牺牲了效率但是保证了数据的安全)。
- - 线程是执行单位,但是不能直接运行,需要先拿到python解释器解释之后才能被cpu执行
- - 同一时刻同一个进程内多个线程无法实现并行,但是可以实现并发
一.GIL全局解释器 垃圾回收机制:
- - 垃圾回收机制也是一个任务,跟你的代码不是串行运行,如果是串行会明显有卡顿
- - 这个垃圾回收到底是开进程还是开线程?肯定是线程,线程肯定也是一段代码,所以想运行也必须要拿到python解释器
- 没有GIL全局解释器锁 他只是对线程加锁 不是对数据
- 运行垃圾回收机制:引用计数 1,必须先拿到python 解释器---> 2.python 进程下的多个线程是并发。若此时你想创建一个 a = 1 cpu运行速度是非常快的
那么就会引发 其他线程垃圾回收机制扫描把我刚创建的内存清理掉 所以必须设置GIL全局解释器锁
也就意味着在Cpython解释器上有一把GIL全局解释器锁
二.
- 1.python中的多线程到底有没有用?
一、数据密集型
二、IO密集型
- #### 1.python中的多线程到底有没有用?
- 单核情况下:四个任务
- 多核情况下:四个任务
- 计算密集型:一个任务算十秒,四个进程和四个线程,肯定是进程快
- IO密集型:任务都是纯io情况下,线程开销比进程小,肯定是线程好
- 一、数据密集型
def task():- res = 0
- for i in range(100000000):
- res = res*i
- if __name__ == '__main__':
- print(os.cpu_count()) #本机内核
- p_list=[]
- start_time= time.time()
- for i in range(4):
- p = Process(target=task) # 进程运行时间为10.636553287506104
- # p = Thread(target= task) # 线程运行时间为19.97660756111145
- p.start()
- p_list.append(p)
- for p in p_list:
- p.join()
- end_time = time.time()
- print('运行时间为%s'% (end_time-start_time))
- ""
- 二、IO密集型
- def work():
- time.sleep(3)
- if __name__ == '__main__':
- print(os.cpu_count())
- start_time =time.time()
- p_list=[]
- for i in range(4):
- # p = Process(target= work) # run is total_time7.271259546279907
- p = Thread(target= work) # run is total_time3.002392053604126
- p.start()
- p_list.append(p)
- for p in p_list:
- p.join()
- end_time =time.time()
- print('run is total_time%s'%(end_time-start_time))
三、全局锁与普通锁
- 对于不同的数据,要想保证安全,需要加不同的锁处理
- GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程
- 保证的是同一个进程下多个线程之间的安全
- """
- from threading import Thread
- import os
- import time
- from threading import Lock
- mutex = Lock()
- num = 100
- def task():
- global num
- mutex.acquire() #抢锁
- temp = num
- time.sleep(0.1)
- num = temp-1
- mutex.release() # 释放锁 开始一个
- if __name__ == '__main__':
- p_lsit=[]
- for i in range(10):
- p = Thread(target=task)
- p.start()
- p_lsit.append(p)
- for p in p_lsit:
- p.join()
- print(num) # 90 相当于10个线程同时去抢100票 必须要确保一个数据同时被10个进程同时抢 锁是起到保护作用 取完一个减一个
四、.死锁与递归锁(了解)
自定义锁一次acquire必须对应一次release,不能连续acquire
递归锁可以连续的acquire,每acquire一次计数加一
- import time
- from threading import Thread,RLock
- mutexA = mutexB= RLock() # 递归锁RLock
- class Mythread(Thread):
- def run(self):
- self.fn1()
- self.fn2()
- def fn1(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 fn2(self):
- mutexB.acquire()
- print('%s 抢到A锁了' % self.name)
- time.sleep(1)
- mutexA.acquire()
- print('%s 抢到B锁了' % self.name)
- mutexA.release()
- print('%s释放了B锁' % self.name)
- mutexB.release()
- print('%s释放了A锁' % self.name)
- if __name__ == '__main__':
- for i in range(100):
- t = Mythread()
- t.start()
五.Event事件
一些线程需要等待另外一些线程运行完毕才能运行,类似于发射信号一样
- from threading import Thread
- from threading import Event
- import time
- event = Event() #造了一个绿灯
- def light():
- print('等红灯')
- time.sleep(3)
- event.set() # 解除阻塞并且给 event发了一个信号
- print('绿灯亮了')
- def car(i):
- print('%s灯红灯'%i)
- event.wait()
- print('%s绿灯了,加油门'%i)
- if __name__ == '__main__':
- t = Thread(target=light)
- t.start()
- p_list=[]
- for i in range(5):
- p = Thread(target=car,args=(i,))
- p.start()
- # 等红灯
- # 0灯红灯
- # 1灯红灯
- # 2灯红灯
- # 3灯红灯
- # 4灯红灯
- # 绿灯亮了
- # 0绿灯了,加油门
- # 1绿灯了,加油门
- # 3绿灯了,加油门
- # 4绿灯了,加油门
- # 2绿灯了,加油门#
六.信号量(了解)
自定义的互斥锁如果是一个厕所,那么信号量就相当于公共厕所,门口挂着多个厕所的钥匙。抢和释放跟互斥锁一致
- 普通互斥锁, 独立卫生间 所有人只有一把锁
- 信号量 ,公共卫生间 有多少个坑 就有多少把锁
- """
- from threading import Thread
- from threading import Semaphore #信号量
- import time
- import random
- sm = Semaphore(5) #一个公共厕所造了5个坑 在厕所外放了5把锁
- def task(name):
- sm.acquire() # 释放信号锁
- print('%s正在蹲坑'%name)
- time.sleep(random.randint(1, 3))
- sm.release()
- for i in range(20):
- t = Thread(target= task,args=('%s伞兵'%i,))
- t.start()
- 0伞兵正在蹲坑
- 1伞兵正在蹲坑
- 2伞兵正在蹲坑
- 3伞兵正在蹲坑
- 4伞兵正在蹲坑
- 此时5个人中 有一个人好了 同时释放了一把锁
- 5伞兵正在蹲坑
- 前面5个好了两个释放给 6,7
- 6伞兵正在蹲坑
- 7伞兵正在蹲坑
- 8伞兵正在蹲坑
- 9伞兵正在蹲坑
- 10伞兵正在蹲坑
- 11伞兵正在蹲坑
- 12伞兵正在蹲坑
- 13伞兵正在蹲坑
- 14伞兵正在蹲坑
- 15伞兵正在蹲坑
- 16伞兵正在蹲坑
- 17伞兵正在蹲坑
- 18伞兵正在蹲坑
- 19伞兵正在蹲坑
七.线程queue
同一个进程下的线程数据都是共享的为什么还要用queue?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()) # 取值
——————》》》
1
2
3- q = queue.LifoQueue(5)
- q.put(1)
- q.put(2)
- q.put(3)
- q.put(4)
- q.put(5)
- print(q.get())
- print(q.get())
- print(q.get())
- print(q.get())
- print(q.get()) # 先进后出 和堆栈一样
——————》》》
5
4
3
2
1
- q = queue.PriorityQueue(3)
- q.put(100,"q")
- q.put(20,"t")
- q.put(-1,'s')
- print(q.get())
- print(q.get())
- print(q.get()) # 优先级是按照数字从小到大排列的
————————》》》
-1
20
100
全局解释锁GIL的更多相关文章
- 并发、并行、同步、异步、全局解释锁GIL、同步锁Lock、死锁、递归锁、同步对象/条件、信号量、队列、生产者消费者、多进程模块、进程的调用、Process类、
并发:是指系统具有处理多个任务/动作的能力. 并行:是指系统具有同时处理多个任务/动作的能力. 并行是并发的子集. 同步:当进程执行到一个IO(等待外部数据)的时候. 异步:当进程执行到一个IO不等到 ...
- python 全局解释锁GIL
Python的全局解释器锁GIL用于保护python解释器,使得任意时刻,只有一个线程在解释器中运行.从而保证线程安全 在多线程环境中,Python 虚拟机按以下方式执行: 1. 设置GIL2. 切换 ...
- 什么是python的全局解释锁(GIL)
GIL解决了Python中的什么问题? 为什么选取GIL作为解决方案? 对多线程Python程序的影响 为什么GIL还没有被删除? 为什么在Python 3 中GIL没有被移除? 如何处理Python ...
- GIL全局解释锁,死锁,信号量,event事件,线程queue,TCP服务端实现并发
一.GIL全局解释锁 在Cpython解释器才有GIL的概念,不是python的特点 在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势. 1.GIL介绍 ...
- 20191031:GIL全局解释锁
20191031:GIL全局解释锁 总结关于GIL全局解释锁的个人理解 GIl全局解释锁,本身不是Python语言的特性,而是Python语言底层的c Python解释器的一个特性.在其他解释器中是没 ...
- ~~并发编程(十一):GIL全局解释锁~~
进击のpython ***** 并发编程--GIL全局解释锁 这小节就是有些"大神"批判python语言不完美之处的开始 这一节我们要了解一下Cpython的GIL解释器锁的工作机 ...
- python 线程队列、线程池、全局解释器锁GIL
一.线程队列 队列特性:取一个值少一个,只能取一次,没有值的时候会阻塞,队列满了,也会阻塞 queue队列 :使用import queue,用法与进程Queue一样 queue is especial ...
- 并发编程——全局解释器锁GIL
1.全局解释器锁GIL GIL其实就是一把互斥锁(牺牲了效率但是保证了数据的安全). 线程是执行单位,但是不能直接运行,需要先拿到python解释器解释之后才能被cpu执行 同一时刻同一个进程内多个线 ...
- 你是否真的了解全局解析锁(GIL)
关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...
随机推荐
- 开始玩矩阵了!先来一道入门题![SDOI2008]递归数列
[SDOI2008]递归数列 题目描述 一个由自然数组成的数列按下式定义: 对于i <= k:ai = bi 对于i > k: ai = c1ai-1 + c2ai-2 + ... + c ...
- E. You Are Given Some Strings...
E. You Are Given Some Strings... AC自动机 求一个串$t$中包含子串$s_{i}+s_{j}$的个数. 可以正反跑两遍AC自动机 正着跑,表示$s_{i}$结束,反正 ...
- Guava 已经学习的代码整理
Guava 已经学习的代码整理 Guava 依赖: compile group: 'com.google.guava', name: 'guava', version: '18.0' 以下是我自己在开 ...
- 关于scroll,client,innear,avail,offset等的理解
在写实例理解scrollWidth,clientWidth,innearWidth,availWidth及offsetWidth等的时候,意外的又发现了margin值合并的问题,在这里同时记录下 1. ...
- 【zabbix】zabbix 高可用架构的实现
https://www.jianshu.com/p/249d47b089b4?utm_campaign=maleskine&utm_content=note&utm_medium=se ...
- 二进制安装MySQL5.6 MySQL5.7
1:系统版本 [root@vhost1 ~]# cat /etc/redhat-release Red Hat Enterprise Linux Server release 6.5 (Santiag ...
- 术语-EDI:EDI
ylbtech-术语-EDI:EDI 电子数据交换(Electronic data interchange,缩写EDI)是指按照同一规定的一套通用标准格式,将标准的经济信息,通过通信网络传输,在贸易伙 ...
- Linux_DNS服务器
目录 目录 DNS DNS Server ServerSite Master DNS Server Forward Domain Reverse Resolution Slave DNS Server ...
- 转发与重定向(forward与redirect)
顾名思义,转发是内部跳转:重定向是重新定向后跳转. 区别: 地址栏显示上: forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器. ...
- 双系统(win10+ubuntu)卸载Ubuntu系统
之前装的双系统,Win10 和Ubuntu ,系统引导使用的是Ubuntu的Grup的引导, 直接删除Ubuntu会导致引导丢失,会很麻烦,win10直接会挂掉,后期恢复需要重建引导 安全删除思路,先 ...