python基础——锁

进程锁 (互斥锁)

进程锁的引入:

模拟抢票程序:

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()

运行结果:

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

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

进程锁的使用:

python 复制代码
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()

运行结果:

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

进程锁再次使用:

不使用进程锁:

python 复制代码
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()

执行结果:

使用进程锁:

python 复制代码
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()方法,会使得程序阻塞,无法继续向下进行

python 复制代码
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()方法

线程锁

线程锁的引入:

python 复制代码
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)

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

部分运行结果:

线程中锁的使用:

python 复制代码
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()操作,才能解锁其他进程

递归锁的使用:

python 复制代码
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并不会进行执行

死锁现象

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

死锁示例:

python 复制代码
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()

部分执行结果:

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

相关推荐
jiuweiC3 小时前
python 虚拟环境-windows
开发语言·windows·python
free-elcmacom3 小时前
机器学习入门<5>支持向量机形象教学:寻找最安全的“三八线”,人人都能懂的算法核心
人工智能·python·算法·机器学习·支持向量机
月亮!3 小时前
人工智能发展现状与未来
人工智能·python·selenium·测试工具·开源·测试用例
天草二十六_简村人3 小时前
jenkins打包制作Python镜像,并推送至docker仓库,部署到k8s
后端·python·docker·容器·kubernetes·jenkins
weixin_457760003 小时前
GIOU (Generalized Intersection over Union) 详解
pytorch·python
汐ya~3 小时前
Cursor连接异常问题“Model not available/connection failed”解决:IDE内置代理配置,无需全局流量代理与虚拟网卡
python·编辑器·cursor
海市公约3 小时前
Python操作SQLite数据库:从基础语法到完整项目实战
数据库·ide·python·程序人生·架构·pycharm·sqlite
轻竹办公PPT3 小时前
学校要求开题报告 PPT,有没有模板?
人工智能·python·powerpoint
神奇的代码在哪里3 小时前
C++的演进与我的编程学习之旅:从底层基础到AI应用
c++·人工智能·python·学习·程序人生·个人开发
lanbo_ai3 小时前
基于深度学习的宠物猫品种识别系统,resnet50,alexnet,mobilenet【pytorch框架,python代码】
人工智能·pytorch·python·深度学习·cnn