当前位置: 首页 > news >正文

gopython 获取python 全局线程锁失败_python线程互斥锁递归锁死锁

一、为什么有了GIL还要给线程加锁

先说一下GIL,所谓的GIL,也叫全局解释器锁,它限制了任何时候都只能有一个线程进入CPU进行计算,所以python所谓的多线程并不能真正的并行。

那为什么有了GIL还需要给线程加锁呢?不是直接一个线程处理完一个数据才轮到下一个线程进行吗?线程锁不是多此一举?

解决这个问题,我们得更深入到底层看看代码是怎么在CPU上运行的。在这里引入一个概念:原子操作

什么是原子操作

所谓的原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,不会运行到一半,然后CPU切换到另外的线程。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱。

像 C语言的i++和python中的+=,-=,*=,/=都不是原子操作,他们在被翻译成机器指令时实际上是分三个步骤的,比如 i-=1 这个操作本质是这样的:

1、先把内存中的1存储在CPU的寄存器中

2、CPU进行计算,减一

3、将寄存器的内容写到内存中。

在1-3这个过程中,线程完全有可能被切换,所以可能导致线程数据的不安全。所以加锁是必要的。我们看看下面的一个例子。

from threading importLock,Thread

n= 10000000

deffunc():globalnfor i in range(1000000):

n-= 1t_lst=[]for i in range(10):

t= Thread(target=func)

t.start()

t_lst.append(t)for i int_lst:i.join()print(n)

上面代码过程就是用十个线程去将一个数减到0,但是运行结果如下:

24a99cd15e278f48890c9cb408a8d38e.png

所以这就验证了线程数据的不安全性。下面是加锁的版本

from threading importLock,Thread

n= 10000000

deffunc(lock):globalnfor i in range(1000000):

lock.acquire()

n= n - 1lock.release()

t_lst=[]

lock=Lock()for i in range(10):

t= Thread(target=func,args=(lock,))

t.start()

t_lst.append(t)for i int_lst:i.join()print(n)

eb4a650fea8fbb724b898e7bc89dc66b.png

二、互斥锁

同一时间只能有一个任务持有互斥锁,而且只有这个任务可以对互斥锁进行解锁。当无法获取锁时,线程进入睡眠等待状态。

其实上面的例子用到的就是互斥锁。当一个线程在操作数据n时候,其他线程是不允许对n进行操作的。

三、死锁

所谓的死锁就是指由多个线程直接,各自持有某些资源,又在申请其他线程所持有的资源,各自坚持着都不释放资源,一直坚持着,这就是死锁。

先不下明确的定义,后面再仔细讨论。我们先来看看一个死锁的例子。

科学家吃面问题:几个科学家一起吃面,必须先申请面和申请到叉子才能开吃。

importtimefrom threading importThread,Lockdefeat1(noodle_lock,fork_lock,name):

noodle_lock.acquire()print(name,'拿到了面')

fork_lock.acquire()print(name,'拿到了叉子')

time.sleep(1)print(name,'吃到了面')

fork_lock.release()

noodle_lock.release()print(name, '放下了面')print(name, '放下了叉子')defeat2(noodle_lock,fork_lock,name):

fork_lock.acquire()print(name, '拿到了叉子')

noodle_lock.acquire()print(name, '拿到了面')print(name, '吃到了面')

noodle_lock.release()print(name, '放下了面')

fork_lock.release()print(name, '放下了叉子')

name_list1= ['特斯拉','牛顿']

name_list2= ['法拉第','爱迪生']

noodle_lock=Lock()

fork_lock=Lock()for i inname_list1:

t= Thread(target=eat1,args=(noodle_lock,fork_lock,i))

t.start()for i inname_list2:

t= Thread(target=eat2, args=(noodle_lock, fork_lock, i))

t.start()

6ccd3f084925b0a5dd4e54b0228d0072.png

一个拿着叉子在等面,一个拿着面在等叉子。一直僵持着,这就是死锁。

四、递归锁

所谓的递归锁就是指一个线程可以多次申请同一把锁,但是不会造成死锁。这就可以用来解决上面的死锁问题

import time

from threading import Thread,RLock

def eat1(noodle_lock,fork_lock,name):

noodle_lock.acquire()

print(name,'拿到了面')

fork_lock.acquire()

print(name,'拿到了叉子')

time.sleep(1)

print(name,'吃到了面')

fork_lock.release()

noodle_lock.release()

print(name, '放下了面')

print(name, '放下了叉子')

def eat2(noodle_lock,fork_lock,name):

fork_lock.acquire()

print(name, '拿到了叉子')

noodle_lock.acquire()

print(name, '拿到了面')

print(name, '吃到了面')

noodle_lock.release()

print(name, '放下了面')

fork_lock.release()

print(name, '放下了叉子')

name_list1 = ['特斯拉','牛顿']

name_list2 = ['法拉第','爱迪生']

noodle_lock=fork_lock = RLock()

for i in name_list1:

t = Thread(target=eat1,args=(noodle_lock,fork_lock,i))

t.start()

for i in name_list2:

t = Thread(target=eat2, args=(noodle_lock, fork_lock, i))

t.start()

49722cb82c077bf594535c878a6dea83.png

下面在仔细讨论一下死锁。

五、死锁产生的四个必要条件

1、互斥条件:当一个进程在访问一个资源的时候,其他进程只能等待。即任何时候一个资源只能给一个进程使用。

2、不可剥夺条件:一个进程在访问一个资源时,其他进程只能等该进程使用完释放资源,不可强行剥夺。

3、请求和保持条件:当一个进程在申请它所需的资源时,并不会释放已有的资源。

4、在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。

只要发生死锁,那么上面四个条件一定都成立。所以只要破坏其中一个,就可以打破死锁。

相关文章:

  • java collections 复制_Java公开课|Java Collections类查复制操作是你学习Java的超车途径,还不来看看就晚了...
  • java 线程的移动问题_Spring Boot中的多线程问题和ThreadLocal
  • Java 经常用到access_用Java连接到Microsoft Access 2007数据库的正确方法是什么?
  • java1.8 interface_JDK1.8新特性——FunctionInterface
  • php file_get_contents 中文,php file_get_contents函数怎么用
  • php 平均下载速度,php限制下载速度的实现方法
  • docker lamp php7,环境准备:docker-compose安装 LAMP、LNMP、php扩展
  • java system.in 怎么写,java 里System.in 输入流如何使用
  • php 两数最大相同子串,用javascript求两个字符串最大的相同的子串(代码实例)...
  • JAVA ulimit,Linux:使用ulimit设置文件最大打开数
  • matlab表示数据散度的统计量,matlab kl-divergence(KL散度)实现代码 | 学步园
  • mysql anzhaung xiangjie,GitHub - dizhaung/spring-boot-student: spring-boot-student
  • php 2m 上传 限制,解决wordpress上传文件2M限制
  • matlab画图的参数,Matlab 画图plot参数 颜色 类型
  • 谱聚类算法 matlab,SpectralClustering 谱聚类算法的matlab实现 238万源代码下载- www.pudn.com...
  • 03Go 类型总结
  • HTML中设置input等文本框为不可操作
  • HTTP中GET与POST的区别 99%的错误认识
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • Python十分钟制作属于你自己的个性logo
  • ReactNative开发常用的三方模块
  • Spark RDD学习: aggregate函数
  • Tornado学习笔记(1)
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 汉诺塔算法
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 浅谈web中前端模板引擎的使用
  • 深度学习入门:10门免费线上课程推荐
  • 收藏好这篇,别再只说“数据劫持”了
  • 【干货分享】dos命令大全
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • (floyd+补集) poj 3275
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (附源码)springboot 智能停车场系统 毕业设计065415
  • (附源码)springboot课程在线考试系统 毕业设计 655127
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • (转载)PyTorch代码规范最佳实践和样式指南
  • .net core 实现redis分片_基于 Redis 的分布式任务调度框架 earth-frost
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • .Net7 环境安装配置
  • .NET开发人员必知的八个网站
  • .net生成的类,跨工程调用显示注释
  • ::前边啥也没有
  • @angular/cli项目构建--Dynamic.Form
  • @JSONField或@JsonProperty注解使用
  • @private @protected @public
  • @取消转义
  • [.net] 如何在mail的加入正文显示图片
  • [100天算法】-x 的平方根(day 61)
  • [2019/05/17]解决springboot测试List接口时JSON传参异常
  • [AIGC 大数据基础]hive浅谈
  • [Angular] 笔记 18:Angular Router
  • [CC2642R1][VSCODE+Embedded IDE+IAR Build+Cortex-Debug] TI CC2642R1基于VsCode的开发环境
  • [codeforces]Levko and Permutation