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服务 ...
随机推荐
- 【Gradle】自定义Android Gradle工程
自定义Android Gradle工程 defaultConfig默认配置 defaultConfig是Android对象中的一个配置项,负责定义所有的默认配置.一个基本的defaultConfig配 ...
- Spring Boot 2 + Thymeleaf:服务器端表单验证
表单验证分为前端验证和服务器端验证.服务器端验证方面,Java提供了主要用于数据验证的JSR 303规范,而Hibernate Validator实现了JSR 303规范.项目依赖加入spring-b ...
- elasticsearch failed to obtain node locks
0x00 事件 重启服务器后,启动 elasticsearch 失败,在日志中观察到以下错误: [2019-10-25T17:29:54,639][WARN ][o.e.b.Elasticsearch ...
- Data Management Technology(3) -- SQL
SQL is a very-high-level language, in which the programmer is able to avoid specifying a lot of data ...
- IDEA使用svn拉取多模块项目
如果没有安装过svn客户端,安装的时候需要选择安装第二个工具,如下图所示 安装小乌龟, 自行搜索, 注意点是需要选择安装第二个工具 因为默认是不安装的, 而这个组件是集成到IDEA ”必须的” . 如 ...
- WD HC510 不能被识别
折腾半天,终于记得去查官方文档
- Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3) C. Messy 构造
C. Messy You are fed up with your messy room, so you decided to clean it up. Your room is a bracket ...
- Sharding-JDBC:单库分表的实现
剧情回顾 前面,我们一共学习了读写分离,垂直拆分,垂直拆分+读写分离.对应的文章分别如下: Sharding-JDBC:查询量大如何优化? Sharding-JDBC:垂直拆分怎么做? 通过上面的优化 ...
- Azure Sphere Development Environment Setup
1. Visual Studio 目前,Visual Studio 2017/2019支持Azure Sphere开发,后续,微软会加入Visual Studio Code的支持.以Visual St ...
- springboot整合shiro进行权限管理
背景:springboot2.1,shiro1.4:由于目前的小项目没做登录,但是客户又需要加上权限,因此楼主就想到了shiro(这是单独的项目,需要集成后台管理系统) shiro简介 Apache ...