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.
"""

(1):python代码的执行由python虚拟机(解释器)来控制,加锁是为了保证同一时刻只有一个线程再运行
用来阻止同一个进程下的多个线程的同时执行(一个进程内多个进程无法实现并行,但可以实现并发)
(2):python解释器有很多种,最常见的就是cpython解释器,内部是由c语言编写的;
GIL本质也是一把互斥锁:将并发变成串行,虽然牺牲了执行效率,但保证了数据的安全性;
GIL的存在是因为Cpython解释器的内存管理不是线程安全的
垃圾回收机制:
1:引用计数(没有被定义使用的)
2:标记清除()
3:分带回收(青春带,老年代)
python的多线程没法利用多核优势 是不是就是没有用了?
看情况讨论,而且肯定是有用的 (3):研究python的多线程是否有用需要分情况讨论(分计算密集型和IO密集型)
四个任务 计算密集型的 10s
单核情况下
开线程更省资源
多核情况下
开进程 10s
开线程 40s 四个任务 IO密集型的
单核情况下
开线程更节省资源
多核情况下
开线程更节省资源 计算密集型:
1、特点:要进行大量的计算,消耗CPU资源。比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力
# 计算密集型
from multiprocessing import Process
from threading import Thread
import os,time def work ():
res = 0
for i in range(10000000):
res *= 1
if __name__ == '__main__':
l=[]
print(os.cpu_count()) # 本机电脑的核数
start = time.time()
for i in range(6):
# p= Thread(target=work) # 开线程 run time is 0.10372185707092285
p=Process(target=work) # 开进程 run time is 0.8038477897644043
l.append(p)
p.start()
for p in l:
p.join()
stop = time.time()
print('run time is %s'%(stop-start))
IO密集型:
特点:CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)
# IO密集型 from multiprocessing import Process
from threading import Thread
import threading
import os
import time def work():
time.sleep(2) if __name__ == '__main__':
l=[]
print(os.cpu_count())
start = time.time()
for i in range(40):
# p = Process(target=work) # 进程耗时 run time is 2.7755727767944336
p=Thread(target=work ) #线程耗时 2.005638360977173
l.append(p)
p.start()
for p in l :
p.join()
stop = time.time() print(' run time is %s'%(stop-start))
ps:数据密集(Data-Intensive)
二、GIL与普通的互斥锁:
from threading import Thread
import time n=100
def task():
global n
tmp = n
time.sleep(2) # IO阻塞等待的 相当于一把锁
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) >> 99/0 """time.sleep(2) # IO操作阻塞等待的 相当于一把锁,
100个中随机第一个抢到锁后,后面的就就进不去了,把这个条件取消掉就能全部依次取完,结果为0 """
三、死锁与递归锁
所谓的死锁:就是指两个或两个以上的进程或线程在执行的时候,因为争夺资源而造成一种互相等待的现象,再也无法执行下去
解决死锁的办法就是利用递归锁(RLock),把Lock 变成RLock
递归锁:就是可以支持在同一线程中多次请求同一资源,简单的来说就是两者共用同一把锁,
当你加锁后释放锁时,另一个同时也可以访问资源并循环的进行加锁(acquire)和 (释放锁)release,直到结束访问为止
from threading import Thread,Lock,RLock,current_thread
import time 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()

关于加锁问题:自己千万不能轻易的处理锁的问题,容易造成死锁现象
四、信号量
信号量 某一段代码,同一时间,只能被N个进程使用
"""
互斥锁:一个厕所(一个坑位)
信号量:公共厕所(多个坑位)
"""
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事件:
一般用在一个子线程等待另一个子线程的时候,与进程的.join反法,主进程等子进程运行完毕后才执行
from threading import Thread,Event # 事件模块
Event几种方法:
event.isSet():返回event的状态值;
event.wait():如果 event.isSet()==False将阻塞线程;
event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
event.clear():恢复event的状态值为False。
等红绿灯事件:
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() >>>>:
伞兵9正在等红灯
绿灯亮了伞兵0加油门飙车了
伞兵3加油门飙车了
伞兵4加油门飙车了
伞兵8加油门飙车了
伞兵1加油门飙车了
伞兵5加油门飙车了
伞兵9加油门飙车了
伞兵6加油门飙车了伞兵7加油门飙车了
六、线程Queue(对列)
queue队列 :使用import queue,用法与进程Queue一样
class queue.Queue(maxsize=0) # 先进先出
class queue.LifoQueue(maxsize=0) #last in fisrt out 后进先出
class queue.PriorityQueue(maxsize=0) # 存储数据时可设置优先级的队列 优先级对列
结果(数字越小优先级越高,优先级高的优先出队):
import queue q=queue.PriorityQueue()
#put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
q.put((20,'a'))
q.put((10,'b'))
q.put((30,'c')) print(q.get())
print(q.get())
print(q.get())
'''
结果(数字越小优先级越高,优先级高的优先出队):
(10, 'b')
(20, 'a')
(30, 'c')
'''
同一个进程下的多个线程本来就是数据共享 为什么还需要队列呢?
因为队列是管道+锁的形式,使用队列就不需要自己手动操作锁的问题了,
锁操作极易造成死锁的现象。
GIL全局解释器锁-死锁与递归锁-信号量-event事件的更多相关文章
- 10 并发编程-(线程)-GIL全局解释器锁&死锁与递归锁
一.GIL全局解释器锁 1.引子 在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势 首先需要明确的一点是GIL并不是Python的特性,它是在实现Pyt ...
- Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures
参考博客: https://www.cnblogs.com/xiao987334176/p/9046028.html 线程简述 什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线 ...
- python 全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)
昨日内容回顾 线程什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线程是什么关系? 线程是在进程中的 一个执行单位 多进程 本质上开启的这个进程里就有一个线程 多线程 单纯的在当 ...
- 同步锁 死锁与递归锁 信号量 线程queue event事件
二个需要注意的点: 1 线程抢的是GIL锁,GIL锁相当于执行权限,拿到执行权限后才能拿到互斥锁Lock,其他线程也可以抢到GIL,但如果发现Lock任然没有被释放则阻塞,即便是拿到执行权限GIL也要 ...
- 线程全局修改、死锁、递归锁、信号量、GIL以及多进程和多线程的比较
线程全局修改 x = 100 def func1(): global x print(x) changex() print(x) def changex(): global x x = 50 func ...
- day 33 什么是线程? 两种创建方式. 守护线程. 锁. 死锁现象. 递归锁. GIL锁
一.线程 1.进程:资源的分配单位 线程:cpu执行单位(实体) 2.线程的创建和销毁开销特别小 3.线程之间资源共享,共享的是同一个进程中的资源 4.线程之间不是隔离的 5.线程可不需 ...
- python并发编程之线程(创建线程,锁(死锁现象,递归锁),GIL锁)
什么是线程 进程:资源分配单位 线程:cpu执行单位(实体),每一个py文件中就是一个进程,一个进程中至少有一个线程 线程的两种创建方式: 一 from threading import Thread ...
- 并发编程~~~多线程~~~守护线程, 互斥锁, 死锁现象与递归锁, 信号量 (Semaphore), GIL全局解释器锁
一 守护线程 from threading import Thread import time def foo(): print(123) time.sleep(1) print('end123') ...
- python 之 并发编程(守护线程与守护进程的区别、线程互斥锁、死锁现象与递归锁、信号量、GIL全局解释器锁)
9.94 守护线程与守护进程的区别 1.对主进程来说,运行完毕指的是主进程代码运行完毕2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕详细解释:1.主 ...
随机推荐
- Python 中 使用 HTMLTestRunner 模块生成测试报告
使用 HTMLTestRunner 模块可以生成测试报告,但是系统自带的报告不详细,不好看,所以找了一份详细的报告 HTMLTestRunner 模板,直接导入就能使用 两种方法生成HTML报告,都 ...
- 全面掌握Nginx配置+快速搭建高可用架构 一 random_index_module 随机主页
修改default.conf 保存重载Nginx,完成配置 自动随机选择主页 string要替换的内容,replacement表示替换后的内容 示例 效果,只替换了第一个 如果要替换所有的,需要用到s ...
- Swift - 从相册中选择视频(过滤掉照片,使用UIImagePickerController)
(本文代码已升级至Swift4) 有时我们需要从系统相册中选择视频录像,来进行编辑或者上传操作,这时使用 UIImagePickerController 就可以实现. 默认情况下,UIImagePic ...
- redis(二)----基本操作
1. redis介绍 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset( ...
- if case for while
#!/bin/basha=$1if [ $a ] #判断$1是否为空then #非空echo "the input is No:$a"exit 0else #空read -p &q ...
- pytorch 数据操作
数据操作 在深度学习中,我们通常会频繁地对数据进行操作.作为动手学深度学习的基础,本节将介绍如何对内存中的数据进行操作. 在PyTorch中,torch.Tensor是存储和变换数据的主要工具.如果你 ...
- 逃生(地图上的dp)
蒜头君在玩一款逃生的游戏.在一个n×m 的矩形地图上,蒜头位于其中一个点.地图上每个格子有加血的药剂,和掉血的火焰,药剂的药效不同,火焰的大小也不同,每个格子上有一个数字,如果格子上的数字是正数说明是 ...
- LeetCode——155. 最小栈
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈. push(x) -- 将元素 x 推入栈中. pop() -- 删除栈顶的元素. top() -- 获取栈顶元素. ...
- ssh到ubuntu没颜色
ssh远程到ubuntu系统, 没有颜色. 原因是 .bashrc 配置没生效. $ echo '. $HOME/.bashrc' > ~/.profile
- 18个Java8日期处理的实践,太有用了
专注于Java领域优质技术,欢迎关注 作者:胖先森 Java 8 推出了全新的日期时间API,在教程中我们将通过一些简单的实例来学习如何使用新API. Java处理日期.日历和时间的方式一直为社区所诟 ...