Python 线程互斥锁 Lock - Python零基础入门教程

目录

  • 一.前言
  • 二.Python 线程共享全局变量
  • 三.Python 线程互斥锁
    • 1.创建互斥锁
    • 2.锁定资源/解锁资源
  • 四.Python 线程死锁
  • 五.重点总结
  • 六.猜你喜欢

一.前言

在前一篇文章 Python 线程创建和传参 中我们介绍了关于 Python 线程的一些简单函数使用和线程的参数传递,使用多线程可以同时执行多个任务,提高开发效率,但是在实际开发中往往我们会碰到线程同步问题,假如有这样一个场景:对全局变量累加 1000000 次,为了提高效率,我们可以使用多线程完成,示例代码如下:

# !usr/bin/env python
# -*- coding:utf-8 _*-
"""
@Author:猿说编程
@Blog(个人博客地址): www.codersrc.com
@File:Python 线程互斥锁 Lock.py
@Time:2021/04/22 08:00
@Motto:不积跬步无以至千里,不积小流无以成江海,程序人生的精彩需要坚持不懈地积累!"""# 导入线程threading模块
import threading# 声明全局变量
g_num = 0def my_thread1():# 声明全局变量global g_num# 循环 1000000 次,每次累计加 1for i in range(0,1000000):g_num = g_num + 1def my_thread2():# 声明全局变量global g_num# 循环 1000000 次,每次累计加 1for i in range(0,1000000):g_num = g_num + 1def main(i):# 声明全局变量global g_num# 初始化全局变量,初始值为 0g_num = 0# 创建两个线程,对全局变量进行累计加 1t1 = threading.Thread(target=my_thread1)t2 = threading.Thread(target=my_thread2)# 启动线程t1.start()t2.start()# 阻塞函数,等待线程结束t1.join()t2.join()# 获取全局变量的值print("第%d次计算结果:%d "% (i,g_num))if __name__ == "__main__":# 循环4次,调用main函数,计算全局变量的值for i in range(1,5):main(i)'''
输出结果:第1次计算结果:1262996
第2次计算结果:1661455
第3次计算结果:1300211
第4次计算结果:1563699
'''

what ? 这是什么操作??看着代码好像也没问题,两个线程,各自累加 1000000 次,不应该输出是 2000000 次吗?而且调用了 4 次 main 函数,每次输出的结果还不同!!

二.Python 线程共享全局变量

分析下上面的代码:两个线程共享全局变量并执行 for 循环 1000000 ,每次自动加 1 ,我们都知道两个线程都是同时在运行,也就是说两个线程同时在执行 g_num = g_num + 1 操作, 经过我们冷静分析一波,貌似结果还是应该等于 2000000 ,对不对?

首先,我们将上面全局变量自动加 1 的代码分为两步:

第一步:g_num + 1
第二步:将 g_num + 1 的结果赋值给 g_num

由此可见,执行一个完整的自动加 1 过程需要两步,然而线程却是在同时运行,谁也不能保证线程 1 的第一步和第二步执行完成之后才执行线程 2 的第一步和第二步,执行的过程充满随机性,这就是导致每次计算结果不同的原因所在!

举个简单的例子:

假如当前 g_num 值是 100,当线程 1 执行第一步时,cpu 通过计算获得结果 101,并准备把计算的结果 101 赋值给 g_num,然后再传值的过程中,线程 2 突然开始执行了并且执行了第一步,此时 g_num 的值仍未 100,101 还在传递的过程中,还没成功赋值,线程 2 获得计算结果 101 ,并准备传递给 g_num ,经过一来一去这么一折腾,分明做了两次加 1 操作,g_num 结果却是 101 ,误差就由此产生,往往循环次数越多,产生的误差就越大。​

三.Python 线程互斥锁

为了避免上述问题,我们可以利用线程互斥锁解决这个问题。那么互斥锁到底是个什么原理呢?互斥锁就好比排队上厕所,一个坑位只能蹲一个人,只有占用坑位的人完事了,另外一个人才能上!

1.创建互斥锁

导入线程模块,通过 threading.Lock 创建互斥锁.

# 导入线程threading模块
import threading# 创建互斥锁
mutex = threading.Lock()

2.锁定资源/解锁资源

  • **acquire **— 锁定资源,此时资源是锁定状态,其他线程无法修改锁定的资源,直到等待锁定的资源释放之后才能操作
  • release释放资源,也称为解锁操作,对锁定的资源解锁,解锁之后其他线程可以对资源正常操作;

以上面的代码为列子:想得到正确的结果,可以直接利用互斥锁在全局变量 加 1 之前 锁定资源,然后在计算完成之后释放资源,这样就是一个完整的计算过程,至于应该是哪个线程先执行,无所谓,先到先得,凭本事说话….演示代码如下:

# !usr/bin/env python
# -*- coding:utf-8 _*-
"""
@Author:猿说编程
@Blog(个人博客地址): www.codersrc.com
@File:Python 线程互斥锁 Lock.py
@Time:2021/04/22 08:00
@Motto:不积跬步无以至千里,不积小流无以成江海,程序人生的精彩需要坚持不懈地积累!"""# 导入线程threading模块
import threading# 声明全局变量
g_num = 0
# 创建互斥锁
mutex = threading.Lock()def my_thread1():# 声明全局变量global g_num# 循环 1000000 次,每次累计加 1for i in range(0,1000000):# 锁定资源mutex.acquire()g_num = g_num + 1# 解锁资源mutex.release()def my_thread2():# 声明全局变量global g_num# 循环 1000000 次,每次累计加 1for i in range(0,1000000):# 锁定资源mutex.acquire()g_num = g_num + 1# 解锁资源mutex.release()def main(i):# 声明全局变量global g_num# 初始化全局变量,初始值为 0g_num = 0# 创建两个线程,对全局变量进行累计加 1t1 = threading.Thread(target=my_thread1)t2 = threading.Thread(target=my_thread2)# 启动线程t1.start()t2.start()# 阻塞函数,等待线程结束t1.join()t2.join()# 获取全局变量的值print("第%d次计算结果:%d "% (i,g_num))if __name__ == "__main__":# 循环4次,调用main函数,计算全局变量的值for i in range(1,5):main(i)'''
输出结果:第1次计算结果:2000000
第2次计算结果:2000000
第3次计算结果:2000000
第4次计算结果:2000000
'''

由此可见,全局变量计算加上互斥锁之后,不论执行多少次,计算结果都相同。注意:互斥锁一旦锁定之后要记得解锁,否则资源会一直处于锁定状态;

四.Python 线程死锁

1.单个互斥锁的死锁:acquire / release 是成对出现的,互斥锁对资源锁定之后就一定要解锁,否则资源会一直处于锁定状态,其他线程无法修改;就好比上面的代码,任何一个线程没有释放资源 release,程序就会一直处于阻塞状态(在等待资源被释放),不信你可以试一试~

2.多个互斥锁的死锁:在同时操作多个互斥锁的时候一定要格外小心,因为一不小心就容易进入死循环,假如有这样一个场景:boss 让程序员一实现功能一的开发,让程序员二实现功能二的开发,功能开发完成之后一起整合代码!

# !usr/bin/env python
# -*- coding:utf-8 _*-
"""
@Author:猿说编程
@Blog(个人博客地址): www.codersrc.com
@File:Python 线程互斥锁 Lock.py
@Time:2021/04/22 08:00
@Motto:不积跬步无以至千里,不积小流无以成江海,程序人生的精彩需要坚持不懈地积累!"""# 导入线程threading模块
import threading
# 导入线程time模块
import time# 创建互斥锁
mutex_one = threading.Lock()
mutex_two = threading.Lock()def programmer_thread1():mutex_one.acquire()print("我是程序员1,module1开发正式开始,谁也别动我的代码")time.sleep(2)# 此时会堵塞,因为这个mutex_two已经被线程programmer_thread2抢先上锁了,等待解锁mutex_two.acquire()print("等待程序员2通知我合并代码")mutex_two.release()mutex_one.release()def programmer_thread2():mutex_two.acquire()print("我是程序员2,module2开发正式开始,谁也别动我的代码")time.sleep(2)# 此时会堵塞,因为这个mutex_one已经被线程programmer_thread1抢先上锁了,等待解锁mutex_one.acquire()print("等待程序员1通知我合并代码")mutex_one.release()mutex_two.release()def main():t1 = threading.Thread(target=programmer_thread1)t2 = threading.Thread(target=programmer_thread2)# 启动线程t1.start()t2.start()# 阻塞函数,等待线程结束t1.join()t2.join()# 整合代码结束print("整合代码结束 ")if __name__ == "__main__":main()'''
输出结果:我是程序员1,module1开发正式开始,谁也别动我的代码
我是程序员2,module2开发正式开始,谁也别动我的代码
'''

分析下上面代码:程序员 1 在等程序员 2 通知,程序员 2 在等程序员 1 通知,两个线程都陷入阻塞中,因为两个线程都在等待对方解锁,这就是死锁!所以在开发中对于死锁的问题还是需要多多注意!

五.重点总结

  • 1.线程与线程之间共享全局变量需要设置互斥锁;
  • 2.注意在互斥锁操作中 **acquire / release **成对出现,避免造成死锁;

六.猜你喜欢

  1. Python for 循环
  2. Python 字符串
  3. Python 列表 list
  4. Python 元组 tuple
  5. Python 字典 dict
  6. Python 条件推导式
  7. Python 列表推导式
  8. Python 字典推导式
  9. Python 函数声明和调用
  10. Python 不定长参数 *argc/**kargcs
  11. Python 匿名函数 lambda
  12. Python return 逻辑判断表达式
  13. Python 字符串/列表/元组/字典之间的相互转换
  14. Python 局部变量和全局变量
  15. Python type 函数和 isinstance 函数区别
  16. Python is 和 == 区别
  17. Python 可变数据类型和不可变数据类型
  18. Python 浅拷贝和深拷贝

未经允许不得转载:猿说编程 » Python 线程互斥锁 Lock

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

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

相关文章

上周五,小编参加了一场高大上的“9”会

上周五,小编参加了一场高大上的“9”会之所以说它是“9”会不仅仅是因为现场的所有工作人员都穿着印有数字“9”的服装看这里还有这里而且整个大会都是围绕着“9”展开的你说,这个大会不叫“9”会,叫什么?之所以说它是“高大上”是…

BugkuCTF-WEB题alert

启动场景: flag{68d5caaa10c5d1007611859e57ad318d} 将框里的unicode编码解码为ASCII即可得到flag flag{68d5caaa10c5d1007611859e57ad318d}

Cloud一分钟 | 谷歌投资的AR云平台开发商Blue Vision Labs,将由美版滴滴Lyft收购

Hello,everyone:10月25日早,星期四,祝大家工作愉快!一分钟新闻时间:完1.微信群:添加小编微信:tangguoyemeng,备注“进群姓名公司职位”即可,加入【云计算学习…

BugkuCTF-WEB题计算器

将maxlength1改为maxlength长度改大即可

北京房租大涨?6个维度,数万条数据帮你揭穿(附详情代码)

作者|丁彦军来源|恋习Python昨天还幻想海边别墅的年轻人,今天可能开始对房租绝望了。8月初,有网友在“水木论坛”发帖控诉长租公寓加价抢房引起关注。据说,一名业主打算出租自己位于天通苑的三居室,预期租金7500元/月,…

BugkuCTF-WEB题eval

基础知识: var_dump() 函数用于输出变量的相关信息。 var_dump() 函数显示关于一个或多个表达式的结构信息,包括表达式的类型与值。数组将递归展开值,通过缩进显示其结构。 此题考察php相关 include是将flag.php文件包含进页面代码 $request…

Cloud一分钟 | 微软Azure云服务宣布又一重磅应用落地;微软云计算转型迅猛,或助其市值短期达到1万亿美元...

Hello,everyone:10月26日早,星期五,祝大家工作愉快!一分钟新闻时间:完1.微信群:添加小编微信:tangguoyemeng,备注“进群姓名公司职位”即可,加入【云计算学习…

BugkuCTF-Reverse题Easy_Re多方法解决

下载附件 首先打开程序: 方法一: 第一步PEID,发现为cpp文件,32位程序 根据flag格式:DUTCTF{} 扔进IDA里,直接在16进制编辑器里AltT搜索CTF字符串 Flag:DUTCTF{We1c0met0DUTCTF} 或者在010editor里查…

云漫圈 | 我觉得我的手机被监听了。。。

参加 2018 AI开发者大会,请点击 ↑↑↑完1.微信群:添加小编微信:tangguoyemeng,备注“进群姓名公司职位”即可,加入【云计算学习交流群】,和志同道合的朋友们共同打卡学习!2.征稿:投…

BugkuCTF-Reverse题Easy_vb多方法解决

先查壳: 我们可以看到这个是Visual Basic 编写的程序,因此,这个是没有加壳的VB程序。 方法一: MCTF{N3t_Rev_1s_E4ay}改为flag{N3t_Rev_1s_E4ay} 方法二: IDA,F5 方法三: OD打开文件 智能…

【有奖倒计时】带你走进AI应用创新大赛十强背后的故事!

时光如梭,截至今日距离?DigiX极客AI应用创新大赛十强争霸投票阶段只剩最后2天联盟君想问下你领到奖品了吗虽然战场无声无息,形势却如火如荼。13天来,我们感受到了众多花粉的热情,也感受到了用户们对高质量AI作品的喜爱。为投票准…

BugkuCTF-PWN题pwn1-瑞士军刀

注意: nc ip 端口 敲完回车没有回显,直接输入ls命令然后cat 文件即可 得到flag

Cloud一分钟 | 加码云计算!IBM斥340亿美元收购Red Hat

Hello,everyone:10月29日早,星期一,祝大家工作愉快!一分钟新闻时间:完1.微信群:添加小编微信:tangguoyemeng,备注“进群姓名公司职位”即可,加入【云计算学习…

Python 线程队列 Queue – FIFO - Python零基础入门教程

目录 一.Python 线程队列分类二.Python 线程先进先出队列 Queue 简介三.Python 线程先进先出队列 Queue 常用函数四.Python 线程先进先出队列 Queue 使用五.猜你喜欢 零基础 Python 学习路线推荐 : Python 学习目录 >> Python 基础入门 之前的文章中讲解很多关于线程间通…

BugkuCTF-WEB题网站被黑

利用御剑扫描后台或者通过dirsearch扫描一下目录看看 得到http://1147.67.246.176:15607/shell.php 打开这个网址 发现需要登录密码 通过Burp添加字典爆破密码为hack 输入得到flag

IBM 收购 RedHat(红帽)!340 亿美元

IBM今天宣布了以高达340亿美元的价格收购Red Hat,这笔交易在开源软件行业和云计算行业势必都会引起地震。Red Hat是将开源软件商业化的先驱之一,其采用的模式是,代码免费提供给其他公司使用,以便重新分发和修改。开源软件已逐渐成…

Python 线程队列 LifoQueue – LIFO - Python零基础入门教程

目录 一.Python 线程队列 Queue 分类二.Python 线程先进后出队列 LifoQueue 简介三.Python 线程先进后出队列 LifoQueue 函数介绍四.Python 线程先进后出队列 LifoQueue 使用五.猜你喜欢 零基础 Python 学习路线推荐 : Python 学习目录 >> Python 基础入门 在 Python 线…

BugkuCTF-WEB题本地管理员

基础知识: base64: 是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一类基于64个可打印字符来表示二进制数据的方法。 XFF伪造请求头: X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的…

你最关心的马蜂窝事件舆论全景图在这里,用文本挖掘一挖到底

原创作者:谭婧,苏格兰折耳喵企业作为社会基本的细胞,在守法盈利的同时,担负着服务消费者、社区、社会等利益相关方的重要责任。互联网企业是互联网时代的宠儿,举手投足备受关注。所谓成也萧何,败也萧何。大…

Kafka日志

位置 server.properties配置文件中通过log.dir指定日志存储目录 log.dir/{topic}-{partition} 核心文件 .log 存储消息的日志文件,固定大小为1G,写满后会新增一个文件,文件名表示当前日志文件记录的第一条消息的偏移量。 .index 以偏移…