GIL全局解释器锁、死锁、递归锁、线程队列
GIL全局解释锁
GIL本质上是一个互斥锁。
GIL是为了阻止同一个进程内多个进程同时执行(并行)
- 单个进程下的多个线程无法实现并行,但能实现并发
这把锁主要是因为Cpython的内存管理不是线程安全的
- 保证线程在执行任务时不会被垃圾回收机制回收
from threading import Thread
import time
num = 100
def task():
global num
num2 = num
time.sleep(1)
num = num2 - 1
print(num)
for line in range(100):
t = Thread(target=task)
t.start()
# 这里的运行结果都是99, 加了IO操作,所有线程都对num进行了减值操作,由于GIL锁的存在,没有修改成功,都是99
多线程的作用
- 计算密集型, 有四个任务,每个任务需要10s
单核:
- 开启进程
- 消耗资源过大
- 4个进程: 40s
- 开启线程
- 消耗资源远小于进程
- 4个线程: 40s
多核:
- 开启进程
- 并行执行, 效率比较高
- 4个进程: 10s
- 开启线程
- 并发执行,执行效率低
- 4个线程: 40s
- IO密集型, 四个任务, 每个任务需要10s
单核:
- 开启进程
- 消耗资源过大
- 4个进程: 40s
- 开启线程
- 消耗资源远小于进程
- 4个线程: 40s
多核:
- 开启进程
- 并行执行, 效率小于多线程, 但是遇到IO会立马切换CPU的执行权限
- 4个进程: 40s + 开启进程消耗的额外时间
- 开启线程
- 并发执行,执行效率高于多进程
- 4个线程: 40s
测试计算密集型
from threading import Thread
from multiprocessing import Process
import time
import os
# 计算密集型
def work1():
number = 0
for line in range(100000000):
number += 1
# IO密集型
def work2():
time.sleep(2)
if __name__ == '__main__':
# 测试计算密集型
print(os.cpu_count()) # 4核cpu
start = time.time()
list1 = []
for line in range(6):
p = Process(target=work1) # 程序执行时间8.756593704223633
# p = Thread(target=work1) # 程序执行时间31.78555393218994
list1.append(p)
p.start()
for p in list1:
p.join()
end = time.time()
print(f'程序执行时间{end - start}')
IO密集型
from threading import Thread
from multiprocessing import Process
import time
import os
# 计算密集型
def work1():
number = 0
for line in range(100000000):
number += 1
# IO密集型
def work2():
time.sleep(1)
if __name__ == '__main__':
# 测试计算密集型
print(os.cpu_count()) # 4核cpu
start = time.time()
list1 = []
for line in range(100):
# p = Process(target=work2) # 程序执行时间15.354223251342773
p = Thread(target=work2) # 程序执行时间1.0206732749938965
list1.append(p)
p.start()
for p in list1:
p.join()
end = time.time()
print(f'程序执行时间{end - start}')
结论:
- 在计算密集型的情况下, 使用多进程
- 在IO密集型的情况下, 使用多线程
- 高效执行多个进程, 内有多个IO密集型程序,使用多进程 + 多线程
死锁现象
指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,如无外力作用,它们都无法推进下去.此时称系统处于死锁状态
以下就是死锁:
from threading import Thread, Lock
from threading import current_thread
import time
mutex_a = Lock()
mutex_b = Lock()
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutex_a.acquire()
print(f'用户{self.name}抢到锁a')
mutex_b.acquire()
print(f'用户{self.name}抢到锁b')
mutex_b.release()
print(f'用户{self.name}释放锁b')
mutex_a.release()
print(f'用户{self.name}释放锁a')
def func2(self):
mutex_b.acquire()
print(f'用户{self.name}抢到锁b')
time.sleep(1)
mutex_a.acquire()
print(f'用户{self.name}抢到锁a')
mutex_a.release()
print(f'用户{self.name}释放锁a')
mutex_b.release()
print(f'用户{self.name}释放锁b')
for line in range(10):
t = MyThread()
t.start()
'''
用户Thread-1抢到锁a
用户Thread-1抢到锁b
用户Thread-1释放锁b
用户Thread-1释放锁a
用户Thread-1抢到锁b
用户Thread-2抢到锁a
'''
# 一直等待
递归锁
用于解决死锁问题
RLock: 比喻成万能钥匙,可以提供给多个人使用
但是第一个使用的时候,会对该锁做一个引用计数
只有引用计数为0, 才能真正释放让一个人使用
上面的例子中用RLock代替Lock, 就不会发生死锁现象
from threading import Thread, Lock, RLock
from threading import current_thread
import time
# mutex_a = Lock()
# mutex_b = Lock()
mutex_a = mutex_b = RLock()
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
mutex_a.acquire()
print(f'用户{self.name}抢到锁a')
mutex_b.acquire()
print(f'用户{self.name}抢到锁b')
mutex_b.release()
print(f'用户{self.name}释放锁b')
mutex_a.release()
print(f'用户{self.name}释放锁a')
def func2(self):
mutex_b.acquire()
print(f'用户{self.name}抢到锁b')
time.sleep(1)
mutex_a.acquire()
print(f'用户{self.name}抢到锁a')
mutex_a.release()
print(f'用户{self.name}释放锁a')
mutex_b.release()
print(f'用户{self.name}释放锁b')
for line in range(10):
t = MyThread()
t.start()
信号量(了解)
互斥锁: 比喻成一个家用马桶, 同一时间只能让一个人去使用
信号比喻成公测多个马桶: 同一时间可以让多个人去使用
from threading import Semaphore
from threading import Thread
from threading import current_thread
import time
sm = Semaphore(5)
def task():
sm.acquire()
print(f'{current_thread().name}执行任务')
time.sleep(1)
sm.release()
for i in range(20):
t = Thread(target=task)
t.start()
线程队列
线程Q: 就是线程队列 FIFO
- 普通队列: 先进先出 FIFO
- 特殊队列: 后进先出 LIFO
- 优先级队列: 若传入一个元组,会依次判断参数的ASCII的数值大小
import queue
# 普通的线程队列: 遵循先进先出
q = queue.Queue()
q.put(1)
q.put(2)
q.put(3)
print(q.get()) # 1
print(q.get()) # 2
# LIFO队列 后进先出
q = queue.LifoQueue()
q.put(1)
q.put(2)
q.put(3)
print(q.get()) # 3
# 优先级队列:根据参数内
q = queue.PriorityQueue()
q.put((4, '我'))
q.put((2, '你'))
q.put((3, 'ta'))
print(q.get()) # (2, '你')
GIL全局解释器锁、死锁、递归锁、线程队列的更多相关文章
- python 线程(创建2种方式,锁,死锁,递归锁,GIL锁,守护进程)
###############总结############ 线程创建的2种方式(重点) 进程:资源分配单位 线程:cpu执行单位(实体) 线程的创建和销毁的开销特别小 线程之间资源共享,是同一个 ...
- day33 线程的创建 验证线程之间共享数据 守护线程 线程进程效率对比 锁 死锁 递归锁
今日内容: 1.线程理论 2.锁: 牺牲了效率,保证了数据的安全(重点) 3.守护线程 4.GIL锁:(重点) 5.计算密集型和IO密集型 6.信号量,事件(了解) 7.补充. 子进程中不能input ...
- 并发编程8 线程的创建&验证线程之间数据共享&守护线程&线程进程效率对比&锁(死锁/递归锁)
1.线程理论以及线程的两种创建方法 2.线程之间是数据共享的与join方法 3.多线程和多进程的效率对比 4.数据共享的补充线程开启太快 5.线程锁 互斥锁 同步锁 6.死锁现象和递归锁 7.守护线程 ...
- python并发编程-多线程实现服务端并发-GIL全局解释器锁-验证python多线程是否有用-死锁-递归锁-信号量-Event事件-线程结合队列-03
目录 结合多线程实现服务端并发(不用socketserver模块) 服务端代码 客户端代码 CIL全局解释器锁****** 可能被问到的两个判断 与普通互斥锁的区别 验证python的多线程是否有用需 ...
- 10 并发编程-(线程)-GIL全局解释器锁&死锁与递归锁
一.GIL全局解释器锁 1.引子 在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势 首先需要明确的一点是GIL并不是Python的特性,它是在实现Pyt ...
- 并发编程~~~多线程~~~守护线程, 互斥锁, 死锁现象与递归锁, 信号量 (Semaphore), GIL全局解释器锁
一 守护线程 from threading import Thread import time def foo(): print(123) time.sleep(1) print('end123') ...
- python 之 并发编程(守护线程与守护进程的区别、线程互斥锁、死锁现象与递归锁、信号量、GIL全局解释器锁)
9.94 守护线程与守护进程的区别 1.对主进程来说,运行完毕指的是主进程代码运行完毕2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕详细解释:1.主 ...
- 并发编程(五)——GIL全局解释器锁、死锁现象与递归锁、信号量、Event事件、线程queue
GIL.死锁现象与递归锁.信号量.Event事件.线程queue 一.GIL全局解释器锁 1.什么是全局解释器锁 GIL本质就是一把互斥锁,相当于执行权限,每个进程内都会存在一把GIL,同一进程内的多 ...
- python基础--GIL全局解释器锁、Event事件、信号量、死锁、递归锁
ps:python解释器有很多种,最常见的就是C python解释器 GIL全局解释器锁: GIL本质上是一把互斥锁:将并发变成串行,牺牲效率保证了数据的安全 用来阻止同一个进程下的多个线程的同时执行 ...
- TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q
TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q 一.TCP协议下的服务端并发 ''' 将不同的功能尽量拆分成不同的函数,拆分出来的功能可以被多个地方使用 TCP服务 ...
随机推荐
- Vue.js+vue-element搭建属于自己的后台管理模板:创建一个项目(四)
Vue.js+vue-element搭建属于自己的后台管理模板:创建一个项目(四) 前言 本章主要讲解通过Vue CLI 脚手架构建工具创建一个项目,在学习Vue CLI之前我们需要先了解下webpa ...
- mysql数据库相关流程图/原理图
mysql数据库相关流程图/原理图 1.mysql主从复制原理图 mysql主从复制原理是大厂后端的高频面试题,了解mysql主从复制原理非常有必要. 主从复制原理,简言之,就三步曲,如下: 主数据库 ...
- STL 中 string 的使用
赋值 string 类型变量可以直接赋值 str = "string"; // str 是 一个 string 类型变量 //等价于 str.assign("string ...
- Fiddler应用——Fiddler过滤功能
Fiddler的过滤功能在Fiddler右面板处,点击Filters显示如图所示面板. 如图所示,Fiddler的过滤面板主要分为几个部分: 1.Use Filters:是否启用过滤器 2.Actio ...
- 机器学习实战之logistic回归分类
利用logistic回归进行分类的主要思想:根据现有数据对分类边界建立回归公式,并以此进行分类. logistic优缺点: 优点:计算代价不高,易于理解和实现.缺点:容易欠拟合,分类精度可能不高. . ...
- 验证登录的前世今生:session、cookie
参考地址:彻底理解cookie,session,token 使用JSON Web Token设计单点登录系统 1.很久很久以前,Web 基本上就是文档的浏览而已, 既然是浏览,作为服务器, 不需要记录 ...
- linux编程fcntl获取和设置文件锁
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types. ...
- HTML5新属性在Google浏览器中不能显示的问题
这两天在学习HTML5新属性时遇到了如下问题,很是不解: 例如在学习使用canvas时,需要绘制一个红色的原点,代码如下: <!DOCTYPE HTML> <html> < ...
- IT兄弟连 Java语法教程 逻辑运算符
表8中显示的布尔逻辑运算符只能操作布尔类型的操作数,所有的二元逻辑运算符都可以组合两个布尔值,得到的结果为布尔类型. 表8 布尔逻辑运算符 布尔逻辑运算符”&“.”|“以及”^“,都会布尔值 ...
- ASP.NET Core使用Nacos作为配置中心的多环境问题
前言 双11那天离职后,这段时间都待在家里,看看书,写写代码,逛逛招聘网站 周一去Gworld面试的时候,有听到面试官说他们用到了配置中心Apollo,聊下来,听他的意思,大概是处理了多环境这个比较方 ...