python基础——锁

进程锁 (互斥锁)

进程锁的引入:

模拟抢票程序:

from multiprocessing import Process
import json
import time
def show_ticket(i):
    with open("./tickets.txt",mode="r",encoding="utf-8") as file:
        ticket = json.load(file)
    print("{}:当前的票数是:{}".format(i,ticket['count']))
def buy_ticket(i):
    with open("./tickets.txt",mode="r",encoding="utf-8") as file:
        ticket = json.load(file)
        if ticket['count'] > 0:
            ticket['count'] -= 1
            print("{}买到票了".format(i))
        else:
            print("票卖完了")
        time.sleep(0.1)
        with open("./tickets.txt",mode="w",encoding="utf-8") as new_file:
            json.dump(ticket,new_file)

if __name__ == '__main__':
    for i in range(10):
        Process(target=show_ticket,args=(i,)).start()
        Process(target=buy_ticket,args=(i,)).start()

运行结果:

实际仅有一张票,但是由于系统的并发执行速度较快,导致系统出现错误

此时,就需要使用锁来进行数据保护,防止出现数据上的错误

进程锁的使用:

from multiprocessing import Process,Lock
import json
import time
def show_ticket(i,lock):
    lock.acquire()
    with open("./tickets.txt",mode="r",encoding="utf-8") as file:
        ticket = json.load(file)
    print("{}:当前的票数是:{}".format(i,ticket['count']))
    lock.release()
def buy_ticket(i,lock):
    lock.acquire()
    with open("./tickets.txt",mode="r",encoding="utf-8") as file:
        ticket = json.load(file)
        if ticket['count'] > 0:
            ticket['count'] -= 1
            print("{}买到票了".format(i))
        else:
            print("票卖完了")
        time.sleep(0.1)
        with open("./tickets.txt",mode="w",encoding="utf-8") as new_file:
            json.dump(ticket,new_file)
        lock.release()
if __name__ == '__main__':
    lock = Lock()
    for i in range(10):
        Process(target=show_ticket,args=(i,lock)).start()
        Process(target=buy_ticket,args=(i,lock)).start()

运行结果:

使用进程锁可以保证一次进运行一个进程,防止进程之间数据的错误

进程锁再次使用:

不使用进程锁:

from multiprocessing import Process
import time
from multiprocessing import Lock
def func1(i,lock):
    # lock.acquire()
    print("函数第{}次执行".format(i))
    time.sleep(1)
    print("函数执行完毕")
    # lock.release()
if __name__ == '__main__':
    lock = Lock()
    for i in range(10):
        Process(target=func1,args=(i,lock)).start()

执行结果:

使用进程锁:

from multiprocessing import Process
import time
from multiprocessing import Lock
def func1(i,lock):
    lock.acquire()
    print("函数第{}次执行".format(i))
    time.sleep(1)
    print("函数执行完毕")

if __name__ == '__main__':
    lock = Lock()
    for i in range(10):
        Process(target=func1,args=(i,lock)).start()

执行结果:

注意:由于前者未使用进程锁,因此十个进程并发执行,总执行时间1s

后者使用进程锁,需要一个进程一个进程进行执行,因此总执行时间为10s

部分代码解释:

lock = Lock()   创建一个锁对象

lock.acquire()   表示该进程拿走锁,然后执行,阻塞其他进程

lock.release()  表示该进程执行完毕,归还锁,使得其他进程得以继续执行

此时,若果进程中只有lock.acquire()方法,而没有lock.release()方法,会使得程序阻塞,无法继续向下进行

from multiprocessing import Process
import time
from multiprocessing import Lock
def func1(i,lock):
    lock.acquire()
    print("函数第{}次执行".format(i))
    time.sleep(1)
    print("函数执行完毕")

if __name__ == '__main__':
    lock = Lock()
    for i in range(10):
        Process(target=func1,args=(i,lock)).start()

执行结果:

另一种写法:

with lock:

代替lock.acquire()方法与lock.release()方法

线程锁              

线程锁的引入:

from threading import Thread
n = 0
def add():
    for i in range(2200000):
        global n
        n += 1
def sub():
    for i in range(2200000):
        global n
        n -= 1

p1 = Thread(target=add)
p1.start()
p2 = Thread(target=sub)
p2.start()
p1.join()
p2.join()
print(n)

运行结果不确定,由于线程的执行速度过快,在同一时间,可能会有多个线程在进行加操作,而在加操作完成后,赋值操作仅进行了一次,因此出现数值的错误,因为错误次数不确定,因此运行的结果也不确定

部分运行结果:

线程中锁的使用:

from threading import Thread,Lock
n = 0
def add(lock):
    for i in range(2200000):
        global n
        lock.acquire()
        n += 1
        lock.release()
def sub(lock):
    for i in range(2200000):
        global n
        lock.acquire()
        n -= 1
        lock.release()
if __name__ == '__main__':
    lock = Lock()
    p1 = Thread(target=add,args=(lock,))
    p1.start()
    p2 = Thread(target=sub,args=(lock,))
    p2.start()
    p1.join()
    p2.join()
    print(n)

此时,无论程序执行多少次,数值的大小如何,由于锁的使用,每次的加运算与赋值运算都是单次进行,因此,数值不会出现错误

执行结果:

递归锁

在一个线程中可以进行多次acquire()操作

但同时,只有进行多次release()操作,才能解锁其他进程

递归锁的使用:

from threading import Thread,Lock,RLock
import time
n = 0
def add(lock):
    for i in range(2):
        global n
        lock.acquire()
        lock.acquire()
        n += 1
        time.sleep(1)
        print("函数add的执行次数{}".format(n))
        lock.release()
def sub(lock):
    for i in range(2):
        global n
        lock.acquire()
        n -= 1
        print("函数sub的执行次数{}".format(n))
        lock.release()
if __name__ == '__main__':
    lock = RLock()
    p1 = Thread(target=add,args=(lock,))
    p1.start()
    p2 = Thread(target=sub,args=(lock,))
    p2.start()
    p1.join()
    p2.join()
    print(n)

执行结果:

此时可以看到,由于函数add中上锁了两次,而只进行了一次解锁,因此在函数add执行完毕之后函数sub并不会进行执行

死锁现象

由于进程的执行顺序不当,或者资源的分配不当而导致整个进程不能进行执行,而陷入卡死的状态

死锁示例:

rom threading import Thread,Lock
import time
noodle_lock = Lock()
fork_lock = Lock()

def eat(name):
    noodle_lock.acquire()
    print("{}抢到面了".format(name))
    fork_lock.acquire()
    print("{}抢到叉子了".format(name))
    print("{}吃面".format(name))
    time.sleep(0.1)
    fork_lock.release()
    print("{}放下了叉子".format(name))
    noodle_lock.release()
    print("{}放下面了".format(name))
def eat2(name):
    fork_lock.acquire()
    print("{}抢到了叉子".format(name))
    noodle_lock.acquire()
    print("{}抢到面了".format(name))
    print("{}吃面".format(name))
    time.sleep(0.1)
    noodle_lock.release()
    print("{}放下面了".format(name))
    fork_lock.release()
    print("{}放下叉子了".format(name))

Thread(target=eat,args=(1,)).start()
Thread(target=eat2,args=(2,)).start()
Thread(target=eat,args=(3,)).start()
Thread(target=eat2,args=(4,)).start()

部分执行结果:

注:死锁现象只发生在多个进程且存在多个互斥锁的情况在,单个互斥锁的合理使用不会导致死锁现象的发生

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

k8s图形化管理工具之rancher

前言 在前面的k8s基础学习中,我们学习了各种资源的搭配运用,以及命令行,声明式文件创建。这些都是为了k8s管理员体会k8s的框架,内容基础。在真正的生产环境中,大部分的公司还是会选用图形化管理工具来管理k8s集群,大大提高工作效率。 在二进制搭建k8集群时,我们就知道了…

Spring依赖注入之setter注入与构造器注入以及applicationContext.xml配置文件特殊值处理

依赖注入之setter注入 在管理bean对象的组件的时候同时给他赋值,就是setter注入,通过setter注入,可以将某些依赖项标记为可选的,因为它们不是在构造对象时立即需要的。这种方式可以减少构造函数的参数数量,使得类的构…

程序员的自我修养:链接、装载与库 6 可执行文件的装载与进程

1 进程虚拟地址空间 PAE 2 装载的方式 2.1 覆盖装入 省略 178 2.2 页映射 3 从操作系统角度看可执行文件的装载 3.1 进程的建立 182

【必剪】鬼畜rap和鬼畜剧场的区别?

在【选择素材】中,每个素材下会有一个标签显示支持哪种的鬼畜形式,在点击一个两种格式的有【鬼畜剧场】和【鬼畜rap】这两中的主要区别在于 【鬼畜剧场】:对素材进行人工编排,创作自己原创的剧情作 【鬼畜rap】:对于素…

IO多路复用-poll(附通信代码)

IO多路复用-poll 1. poll函数 和select函数的比较 内核对应文件描述符的检测也是以线性的方式进行轮询,根据描述符的状态进行处理poll和select检测的文件描述符集合会在检测过程中频繁的进行用户区和内核区的拷贝,它的开销随着文件描述符数量的增加而…

恒峰配网行波型故障预警定位装置特点及优势

随着电力系统的不断发展,电网运行的安全性和稳定性对于国家经济和人民生活至关重要。为了提高电网运行的可靠性,减少故障发生的可能性,我国电力行业不断引进新技术、新设备,其中配网行波型故障预警定位装置在电网安全领域发挥着越…

CentOS安装Redis教程-shell脚本一键安装配置

文章目录 前言一、Redis单机版安装教程1. 复制脚本2. 增加执行权限3. 执行脚本 二、Redis扩展集群版安装教程1. 安装Redis单机版2. 复制脚本3. 增加执行权限4. 执行脚本5. 测试6. redis_cluster.sh 命令6.1 启动Redis扩展集群6.2 停止Redis扩展集群6.3 查看Redis扩展集群节点信…

mysql 基础(三)

一、多表设计 数据库设计范式 第一范式(确保每列保持原子性) 第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。第二范式就是要有主键,要求其他字段都依赖于主键。 没有主键就没有唯一性&…

知识产权实缴注册资金的流程

随着新《公司法》的出台,很多企业老板几乎睡不着,都在为实缴注册资本苦恼。前文有谈到目前比较靠谱的实缴方式是知识产权实缴。那么知识产权实缴的流程是怎么样的?需要准备哪些资料? 下面用一张图为各位企业老板们解读知识产权实…

ios上架缺少info.plist文件

app打包ios提示修改info.plist文件,在iOS原生开发中提供了配置 Info.plist 和 资源文件(Bundle Resources)。uni-app中对常用项进行了封装,提供了manifest.json。 但manifest.json不能包含所有iOS的配置。需要自定义一个Info.plis…

掌握大语言模型技术: 推理优化

掌握大语言模型技术_推理优化 堆叠 Transformer 层来创建大型模型可以带来更好的准确性、少样本学习能力,甚至在各种语言任务上具有接近人类的涌现能力。 这些基础模型的训练成本很高,并且在推理过程中可能会占用大量内存和计算资源(经常性成…

【本科生机器学习】【北京航空航天大学】课题报告:支持向量机(Support Vector Machine, SVM)初步研究【上、原理部分】

说明: (1)、仅供个人学习使用; (2)、本科生学术水平有限,故不能保证全无科学性错误,本文仅作为该领域的学习参考。 一、课程总结 1、机器学习(Machine Learning, ML&am…

【9.DAC数模转换器】蓝桥杯嵌入式一周拿奖速成系列

系列文章目录 蓝桥杯嵌入式系列文章目录(更多此系列文章可见) DAC数模转换器 系列文章目录一、STM32CUBEMX配置二、项目代码1.main.c --> DACProcess 总结 一、STM32CUBEMX配置 STM32CUBEMX PA4 -> DAC1_OUT1 ; PA5 -> DAC1_OUT2DACProcess 二、项目代码 1.main.c -…

司铭宇老师:销售人员心态激励培训:销售心态调整与情绪压力管理

销售人员心态激励培训:销售心态调整与情绪压力管理:迈向成功的关键要素 导语:在竞争激烈的销售行业中,心态调整与情绪压力管理成为销售人员至关重要的能力。如何在这场博弈中保持良好的心态,有效应对压力,…

C++的关键字,命名空间,缺省参数,函数重载以及原理

文章目录 前言一、C关键字(C98)二、命名空间命名空间介绍命名空间的使用 三、C输入【cin】& 输出【cout】四、缺省参数缺省参数概念缺省参数分类缺省参数的使用小结一下 五、函数重载函数重载介绍函数重载类型 六、C支持函数重载的原理--名字修饰(name Mangling)【重点】 前…

科普栏目|负氧离子水壁炉低能耗的背后的原因与生活优势

科普栏目|负氧离子水壁炉低能耗的背后的原因与生活优势 在当今追求绿色生活和能源高效利用的潮流中,负氧离子水壁炉以其低能耗的特性成为了家庭装饰领域的一颗明珠。究竟是什么原因使得这项技术在能耗方面脱颖而出呢?而低能耗又能为生活带来…

freeswitch智能外呼系统搭建流程

1.获取实时音频数据 media_bug (好多mrcp方式也崩溃所以用以下方式) 可以参考 方式可以通过socket或者webscoket freeswitch[1.05]用websocket发送mediabug语音流到ASRProxy实现实时质检和坐席辅助 - 知乎 2.webscoket 好多c的库放模块容易崩溃 可以…

适合孩子读书用什么的落地灯?落地护眼灯测评推荐

现代人用眼负荷极大,不仅白天要办公,晚上更是玩手机、刷短视频或是晚间看书阅读,所以营造一个健康的照明环境很重要! 如何营造一个健康的照明环境呢?那就不得不提起最近就很火的落地护眼灯。不同于其他台灯灯具&#…

flutter极光推送配置厂商通道(华为)笔记--进行中

一、基础集成按照下面官方文档进行 厂商通道相关参数申请教程 集成厂商 集成指南 官方文档:厂商通道回执配置指南 注意:不同厂商对app上架的要求不同,华为、荣耀 对app上架没有硬性要求 遇到问题 1、引入apply plugin: com.huawei.agconn…

Java玩转《啊哈算法》排序之桶排序

过去心不可得,现在心不可得,未来心不可得 目录在这里 楔子代码地址桶排序代码核心部分优缺点 完整代码演示 升级版核心代码完整代码演示 楔子 大家好!本人最近看了下《啊哈算法》,写的确实不错,生动形象又有趣&#x…