python线程同步锁_[python] 线程间同步之Lock RLock

为什么需要同步

同样举之前的例子,两个线程分别对同一个全局变量进行加减,得不到预期结果,代码如下:

total = 0

def add():

global total

for i in range(1000000):

total += 1

def desc():

global total

for i in range(1000000):

total -= 1

import threading

thread1 = threading.Thread(target=add)

thread2 = threading.Thread(target=desc)

thread1.start()

thread2.start()

thread1.join()

thread2.join()

print(total)

原因就是因为 += 和 -=并不是原子操作。可以使用dis模块查看字节码:

import dis

def add(total):

total += 1

def desc(total):

total -= 1

total = 0

print(dis.dis(add))

print(dis.dis(desc))

# 运行结果如下:

# 3 0 LOAD_FAST 0 (total)

# 3 LOAD_CONST 1 (1)

# 6 INPLACE_ADD

# 7 STORE_FAST 0 (total)

# 10 LOAD_CONST 0 (None)

# 13 RETURN_VALUE

# None

# 5 0 LOAD_FAST 0 (total)

# 3 LOAD_CONST 1 (1)

# 6 INPLACE_SUBTRACT

# 7 STORE_FAST 0 (total)

# 10 LOAD_CONST 0 (None)

# 13 RETURN_VALUE

# None

可以看到 add()函数虽然其中只有一行代码,但是字节码主要分为四个步骤:

load 变量total

load 常量 1

执行加法操作

对total进行赋值

同理,desc()函数的步骤相同,只是第三步改为执行减法。

假设一种极端情况,开始total = 0,首先线程1 load 变量total,得到值为0,切换到线程2,同样的到total为0,再次切换线程1 load常量1,执行加法,给total赋值得到1;然后线程2也 laod常量1,执行减法,给total赋值为-1。最终total为-1,而不是预期的0。

期望中,必须在+=操作结束后,才能执行-=,所以线程同步的需求就出来了。

互斥锁Lock

threading模块中提供了threading.Lock类(互斥锁),基本用法如下:

import threading

lock = threading.Lock()

lock.acquire() # 获取锁

# dosomething…… # 临界区的代码只能被同时只能被一个线程运行

lock.release() # 释放锁

将上面的代码修改,即可得到正确结果:

import threading

total = 0

lock = threading.Lock()

def add(lock):

global total

for i in range(1000000):

lock.acquire()

total += 1

lock.release()

def desc(lock):

global total

for i in range(1000000):

lock.acquire()

total -= 1

lock.release()

thread1 = threading.Thread(target=add, args=(lock,))

thread2 = threading.Thread(target=desc, args=(lock,))

thread1.start()

thread2.start()

thread1.join()

thread2.join()

print(total)

# 执行结果 0

可以看到,添加互斥锁之后,程序执行结果是正确的,但是用了互斥锁之后,同样有一些缺陷:

添加锁之后,会影响程序性能。

可能引起"死锁"。

死锁主要在两种情况下发生:

迭代死锁

一个线程“迭代”请求同一个资源 ,会造成死锁。

lock = threading.Lock()

lock.acquire()

lock.acquire()

total += 1

lock.release()

lock.release()

上例种,第一次请求资源后还未 release ,再次acquire,最终无法释放,造成死锁 。(可通过可重入锁解决这个问题)。

互相调用死锁

两个线程中都会调用相同的资源,互相等待对方结束的情况 。假设A线程需要资源a,b,B线程也需要资源a,b,而线程A先获取资源a后,再获取资源b, B线程先获取资源b,再获取资源a。在A线程获取资源a,B线程获取资源b后,A线程在等待B线程释放资源b,而B线程在等待A线程释放资源a,从而死锁就发生了。

import threading

import time

lock_a = threading.Lock()

lock_b = threading.Lock()

def func1():

global lock_a

global lock_b

lock_a.acquire()

time.sleep(1)

lock_b.acquire()

time.sleep(1)

lock_b.release()

lock_a.release()

def func2():

global lock_a

global lock_b

lock_b.acquire()

time.sleep(1)

lock_a.acquire()

time.sleep(1)

lock_a.release()

lock_b.release()

thread1 = threading.Thread(target=func1)

thread2 = threading.Thread(target=func2)

thread1.start()

thread2.start()

thread1.join()

thread2.join()

print("program finished")

# 程序会陷入死循环

这个例子比较重要,开始理解错了,如果B线程获取了资源b,然后释放之后再获取资源a,这样是不会发生死锁的。只有在B线程获取了资源b,还没有释放的时候,获取了资源a,才会发生死锁。

可重入锁RLock

为解决同一线程种不能多次请求同一资源的问题,python提供了“可重入锁”:threading.RLock,RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源 。用法和threading.Lock类相同。

将上面迭代死锁的代码改写一下,就不会发生死锁,但注意,调用acquire和release的次数必须相等。

lock = threading.RLock()

lock.acquire()

lock.acquire()

total += 1

lock.release()

lock.release()

一般不会写这么无聊的代码,但是有一种情况是可能发生的,在加锁区域调用了某个函数,而这个函数内部又申请了同样的资源。

lock = threading.RLock()

def dosomething(lock):

lock.acquire()

# do something

lock.release()

lock.acquire()

dosomething(lock)

lock.release()

总结

线程间访问同一变量需要同步。

线程间加锁会导致性能损失。

加锁可能产生死锁,迭代死锁和互相调用死锁。

可重入锁可以避免迭代死锁。

参考

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/542520.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

servlet的由来

2019独角兽企业重金招聘Python工程师标准>>> 动静态网页技术 首先说下访问网页的大概过程: 你在浏览器中输入网址,按下enter键,此时浏览器代你做了很多事,简要说为:将你输入的这个网址作为目的地参数&#…

php header 文件大小,php获取远程文件大小及信息的函数(head_php

php获取远程文件大小及信息的函数(header头信息获取)阿里西西Alixixi.com开发团队在做一个客户系统时,需要做远程下载的功能,并实时显示进度条效果。所以,需要预先读取远程文件的大小信息,然后做为实时下载进度条的参数。功能函数…

Java ObjectInputStream readUnsignedShort()方法(带示例)

ObjectInputStream类readUnsignedShort()方法 (ObjectInputStream Class readUnsignedShort() method) readUnsignedShort() method is available in java.io package. readUnsignedShort()方法在java.io包中可用。 readUnsignedShort() method is used to read 2 bytes (i.e. …

python中info的用法_Python pandas.DataFrame.info函数方法的使用

DataFrame.info(self, verboseNone, bufNone, max_colsNone, memory_usageNone, null_countsNone) [source] 打印DataFrame的简要摘要。 此方法显示有关DataFrame的信息,包括索引dtype和列dtype,非空值和内存使用情况。 参数:verbose &#x…

第四次作业 孙保平034 李路平029

用C编写一个学生成绩管理系统 1、可以实现以下功能&#xff1a; cout<<"〓〓〓〓〓〓〓〓〓★ ☆ 1.增加学生成绩 ☆ ★〓〓〓〓〓〓〓〓〓"<<endl; 2、用链表存储信息 * 程序头部的注释结束 3、约定的规范&#xff1a; 1界面设计简介&#xff0c;人性化…

php serialize error at offset,PHP Notice: unserialize(): Error at offset XX of XX bytes

之前同事在本地开发的时候&#xff0c;出现一个错误&#xff0c;如下图所示&#xff1a;字面意思就是反序列化错误&#xff0c;由此bug引申出来序列化和反序列化得应用&#xff0c;以及php array当key为string类型的数字值时&#xff0c;会发生什么情形。先来看序列化$str [1 …

Java ClassLoader setClassAssertionStatus()方法与示例

ClassLoader类setClassAssertionStatus()方法 (ClassLoader Class setClassAssertionStatus() method) setClassAssertionStatus() method is available in java.lang package. setClassAssertionStatus()方法在java.lang包中可用。 setClassAssertionStatus() method is used …

python怎么变各种颜色_python – 如何淡化颜色

有很多方法可以做到这一点.您如何选择这取决于您是否重视速度和简单性或感知均匀性.如果你需要它是真正统一的,你需要用颜色配置文件定义RGB颜色,你需要配置文件的原色,这样你就可以转换为XYZ,然后转换到LAB,你可以操作L通道. 大多数情况下,您不需要这样做,而是可以使用像Photo…

informatica中元数据管理

摘自&#xff1a; http://blog.itpub.net/28690368/viewspace-766528/ informaica是一个很强大的ETL工具&#xff0c;WORKFLOW MANAGER负责对ETL调度流程进行设计与管理和执行&#xff0c;informatica在资料库中提供以下表来存储调动流程的相关信息&#xff0c;以便WORKFLOW …

yii+php+当前目录,Yii应用的目录结构和入口脚本

以下是一个通过高级模版安装后典型的Yii应用的目录结构&#xff1a;~~~.├── backend├── common├── console├── environments├── frontend├── nbproject├── tests├── vendor├── composer.json├── composer.lock├── init├── init.bat├── …

8086 寻址方式_8086微处理器的不同寻址模式

8086 寻址方式Introduction: 介绍&#xff1a; Addressing mode tells us what is the type of the operand and the way they are accessed from the memory for execution of an instruction and how to fetch particular instruction from the memory. There are mainly 8 …

决策树的value是什么意思_从零开始的机器学习实用指南(六):决策树

类似SVM&#xff0c;决策树也是非常多功能的机器学习算法&#xff0c;可以分类&#xff0c;回归&#xff0c;甚至可以完成多输出的任务&#xff0c;能够拟合复杂的数据集&#xff08;比如第二章的房价预测例子&#xff0c;虽然是过拟合了。&#xff09;决策树也是很多集成学习的…

Hive中生成随机唯一标识ID的方法

2019独角兽企业重金招聘Python工程师标准>>> HIVE中处理的数据往往比较多&#xff0c;在处理数据的时候希望给处理得到的数据一个ID标识&#xff0c;这时候可以用到UUID。 UUID的算法的核心思想是结合机器的网卡、当地时间、一个随即数来生成UUID。从理论上讲&#…

php从网页获得数据,php根据URL获得网页内容

php 中根据url来获得网页内容非常的方便&#xff0c;可以通过系统内置函数file_get_contents(),传入url,即可返回网页的内容&#xff0c;比如获得百度首页的内容代码为&#xff1a;$html file_get_contents(http://www.baidu.com/);echo $html;就可以显示出百度首页的内容&…

2020知道python语言应用答案_2020知到Python语言应用答案章节期末答案

组合管理理论最早由哈维马科维兹于1962年系统的提出&#xff0c;他开创了对投资进行整体管理的先 公司型基金和契约型基金的区别&#xff0c;下列不包括&#xff08;&#xff09;。 A&#xff0e;资金的性质B&#xff0e;基金的营运依据C&#xff0e;基 我国&#xff08;&#…

如何在Bootstrap中使用Jumbotron和页面标头类?

Introduction 介绍 In the previous article, we have learned how Responsive column, Nesting Columns and offset Columns work and how to use them? I hope now, you all are comfortable with the grid system; what is it, how to use it and how we can use it for c…

python中的数字类型格式与运算,python数字数据类型

python数字数据类型1. 数字在我们很小的时候&#xff0c;父母便开始教我们数数&#xff0c;从1数到10&#xff0c;聪明的孩子可以数的更多。python支持3中数值类型整型(int)&#xff0c;通常称之为整型或整数&#xff0c;这个概念与我们小学时学过的整数是相同的&#xff0c;py…

getprocaddress得到为0_基于ZU+系列MPSoC芯片的USB3.0/2.0接口硬件设计

本文主要介绍Zynq UltraScale MPSoC系列芯片的USB3.0/2.0接口硬件设计。ZU系列MPSoC要实现USB3.0/2.0的全部功能&#xff0c;需要同时使用MIO和GTR。因为GTR接口中的USB接口只支持USB3.0&#xff0c;对USB2.0的支持需要通过MIO接口外接USB PHY实现。ZU系列MPSoC包括两个USB接口…

如何设置单词第一个字母大写_大写一行中每个单词的第一个和最后一个字母

如何设置单词第一个字母大写Problem statement: 问题陈述&#xff1a; Given an input line, capitalize first and last letter of each word in the given line. Its provided that the input line is in lowercase. 给定输入行&#xff0c; 将给定行中每个单词的第一个和最…

php如何编造简历,在简历里编造内容需要注意哪些问题?

在个人简历里编造内容可得有一定依据才行&#xff0c;总得为自己后期做个准备工作是不是&#xff1f;你编造的东西不只是给企业看一看而已&#xff0c;企业还会对这些内容作出进一步的判断&#xff0c;并且可能就其对你进行提问&#xff0c;如果你答不出来而曝光自己是在欺骗企…