线程互斥锁、进程互斥锁、死锁,递归锁

news/2024/5/20 14:06:38 标签: lock, metux, RLock, 互斥锁, 死锁, 递归锁
lock" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);">

1 线程互斥锁
2 进程互斥锁
3 死锁
4 递归锁

1 线程互斥锁


线程互斥锁(Mutex,全名 Mutual Exclusion)是多线程编程中一种用于同步线程访问共享资源的机制。
在并发编程中,当多个线程同时访问共享的数据或资源时,可能会导致不确定的行为,
因为一个线程的操作可能与其他线程的操作交叉进行,导致数据不一致或错误。

互斥锁通过确保在任何时刻只有一个线程可以访问共享资源来解决这个问题。
当一个线程获得了互斥锁(锁定),其他线程就必须等待,直到这个线程释放锁。
from threading import Thread
from multiprocessing import Lock

import time

money = 100
mutex = Lock()  # 加锁编程串行


def task():
    global money
    # 获取数据的时候进行抢锁
    mutex.acquire()
    tmp = money
    time.sleep(0.1)
    money = tmp - 1
    mutex.release()


if __name__ == '__main__':

    t_list = []
    for i in range(100):
        t = Thread(target=task)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()

    print(money)

2 进程互斥锁


进程互斥锁是一种用于多进程编程的同步原语,用于保护共享资源,确保在任何时刻只有一个进程可以访问
这些资源。在多进程环境中,由于进程之间拥有独立的内存空间,因此需要一种机制来协调它们对共享资源
的访问,以避免数据竞争和不一致的状态。

作用:
保护共享资源: 进程互斥锁用于确保在任何给定时刻只有一个进程可以访问共享资源。
这对于防止数据竞争和维护一致性非常重要。

同步进程: 进程互斥锁还可以用于同步进程的执行。
例如,一个进程可能需要等待另一个进程完成某个任务之后再继续执行。
import time
from multiprocessing import Process, Lock
import json
import random


# 查票
def search(i):
    # 文件操作读取票数
    with open('data', 'r', encoding='utf8') as f:
        dic = json.load(f)
    print(f'用户{i}查询余票{dic.get("ticket_num")}')


# 买票 先查再买
def buy(i):
    # 文件操作读取票数
    with open('data', 'r', encoding='utf8') as rf:
        dic = json.load(rf)
    # 模拟网络延迟
    time.sleep(random.randint(1, 3))
    # 判断当前是否有票
    if dic.get('ticket_num') > 0:
        # 修改数据库 买票
        dic['ticket_num'] -= 1
        # 写入数据库
        with open('data', 'w', encoding='utf8') as wf:
            json.dump(dic, wf)
        print(f'用户{i}买票成功')
    else:
        print(f'用户{i}没有抢到票')


# 整合上面两个函数
def run(i, mutex):
    search(i)
    # 给买票环节加锁处理
    # 抢锁
    mutex.acquire()

    buy(i)
    # 释放锁
    mutex.release()


if __name__ == '__main__':
    # 在主进程中生成一把锁,让所有的子进程抢,谁先抢到谁买票
    mutex = Lock()
    for i in range(1, 11):
        p = Process(target=run, args=(i, mutex))
        p.start()

"""
多个进程操作同一份数据的时候,会出现数据错乱的问题
针对上述问题,解决方式就是加锁处理:将并发编程串行,牺牲效率但是保证了数据的安全。
"""

lock3_h1_127">

lock">3 死锁

***死锁***是指两个或多个进程在执行过程中,因争夺资源而造成的一种互相等待的现象,
若无外力作用,它们都将无法推进下去。

死锁的产生通常包含以下几个必要条件,称为死锁的四个必要条件:

1. **互斥条件(Mutual Exclusion):** 一个资源每次只能被一个进程使用。
2. **请求与保持条件(Hold and Wait):** 一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3. **不剥夺条件(No Preemption):** 进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时自己释放。
4. **环路等待条件(Circular Wait):** 若干进程之间形成一种循环等待资源的关系。

这四个条件同时满足时,就可能发生死锁**解决死锁的方法:**

1. **预防死锁** 通过破坏死锁的四个必要条件中的一个或多个,来预防死锁的发生。
   例如,使用资源的有序分配,使得每个进程按照一定的顺序获取资源,防止环路等待。

2. **避免死锁** 在系统运行时采取一些策略来避免死锁。
   银行家算法是一种典型的避免死锁的算法,通过判断每次资源请求是否使系统处于安全状态,
   来避免死锁的发生。

3. **检测死锁** 允许死锁的发生,但是在发生死锁时,能够及时地检测到并采取措施解除死锁。
   操作系统可以周期性地检测系统中是否有死锁发生,一旦检测到死锁,
   就通过终止某些进程或者撤销某些资源的分配来解除死锁4. **解除死锁** 当系统检测到死锁存在时,可以通过撤销进程或者回收资源等手段来解除死锁死锁是多任务系统中常见的问题,需要在系统设计和运行中采取合适的措施来防范和处理。
from threading import Thread, Lock

import time

mutexA = Lock()
mutexB = Lock()


# 类只要加括号多次,产生的肯定是不同的对象
# 如果你想要实现多次加括号等到的是相同的对象,单例模式

class MyThread(Thread):
    def run(self):
        self.func1()
        self.func2()

    def func1(self):
        mutexA.acquire()
        print(f'{self.name}抢到了A锁')  # 获取当前线程名
        mutexB.acquire()
        print(f'{self.name}抢到了B锁')  # 获取当前线程名
        mutexB.release()
        mutexA.release()

    def func2(self):
        mutexB.acquire()
        print(f'{self.name}抢到了B锁')  # 获取当前线程名
        time.sleep(2)
        mutexA.acquire()
        print(f'{self.name}抢到了A锁')  # 获取当前 线程名
        mutexA.release()
        mutexB.release()


if __name__ == '__main__':
    for i in range(10):
        t = MyThread()
        t.start()

4 递归锁


递归锁(Recursive Lock)是一种特殊类型的互斥锁,也称为可重入锁。
它允许同一线程在多次请求时成功地获取锁,而不会造成死锁。当一个线程持有一个递归锁时,
它可以再次获取自己已经持有的锁,而不会发生阻塞。

递归锁在多层递归函数中可以避免死锁的发生。在一个函数内部调用自己,如果没有递归锁,
可能会因为锁的重复获取而导致死锁递归锁保证了在同一线程内对于同一个递归锁的重复加锁和解锁是安全的。

使用递归锁的主要场景是在复杂的程序结构中,可能存在多层嵌套的函数调用,这时如果需要在这些函数之间
使用锁来保护共享资源,递归锁就能够方便地解决同一线程中多次获取锁的问题。

"""
递归锁的特点:
    可以被连续的acquire和release
    但是只能被第一个抢到这把锁执行上述操作
    它的内部有一个计数器,每acquire一次计数加1,每release一次计数减1
    只要计数不为0,那么其他人都无法抢到该锁
"""
from threading import Thread, Lock, RLock

import time

mutexA = mutexB = RLock()


class MyThread(Thread):
    def run(self):
        self.func1()
        self.func2()

    def func1(self):
        mutexA.acquire()
        print(f'{self.name}抢到了A锁')  # 获取当前线程名
        mutexB.acquire()
        print(f'{self.name}抢到了B锁')  # 获取当前线程名
        mutexB.release()
        mutexA.release()

    def func2(self):
        mutexB.acquire()
        print(f'{self.name}抢到了B锁')  # 获取当前线程名
        time.sleep(2)
        mutexA.acquire()
        print(f'{self.name}抢到了A锁')  # 获取当前 线程名
        mutexA.release()
        mutexB.release()


if __name__ == '__main__':
    for i in range(10):
        t = MyThread()
        t.start()




http://www.niftyadmin.cn/n/5186300.html

相关文章

Antv/G2 柱状图添加自定义点击事件

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>柱状图点击事件</title></head><body><div id"container" /><script src"https://gw.alipayobjects.com/os/lib/antv/g2/4.2.8/…

golang 上传图片 --chatGPT

问&#xff1a;makeImgUpload(path string) 实现发送发送图片&#xff0c; 发送类型为 multipart/form-data gpt: 下面是一个简单的 makeImgUpload 函数的实现&#xff0c;用于发送图片并以 multipart/form-data 格式进行上传。请注意&#xff0c;此代码假设图片文件路径是正确…

geoserver面的填充样式错误记录

qgis生成如上的sld格式文件后发布到geoserver填充色没有问题&#xff0c;但是填充的斜线显示不出来&#xff0c;需要进行替换&#xff0c;例如 <se:WellKnownName>horline</se:WellKnownName> 替换成 <se:WellKnownName>shape://backslash</se:WellKnown…

优化收益与用户体验:游戏APP需接入的广告类型

随着移动互联网的蓬勃发展&#xff0c;游戏APP作为一个重要的娱乐载体&#xff0c;越来越受到用户的欢迎。然而&#xff0c;对于游戏开发者来说&#xff0c;如何在保持用户体验的同时实现收益最大化成为了一项挑战。广告作为一种主要的收入来源&#xff0c;游戏APP需要巧妙地选…

【献给过去的自己】栈实现计算器(C语言)

背景 记得在刚学C语言时&#xff0c;写了一篇栈实现计算器-CSDN博客文章。偶然间看到了文章的阅读量以及评论&#xff0c;居然有1.7w的展现和多条博友的点评&#xff0c;反馈。 现在回过头来看&#xff0c;的确有许多不严谨的地方&#xff0c;毕竟当时分享文章时&#xff0c;还…

侧面多级菜单(一个大类、一个小类、小类下多个物体)

效果&#xff1a; 说明&#xff1a; 左右侧面板使用Animator组件控制滑入滑出。左侧面板中&#xff0c;左的左里面是大类&#xff0c;左的右有绿色的小类&#xff0c;绿色的小类下有多个真正的UI图片按钮。 要点&#xff1a; 结合了一点EasyGridBuilderPro插件的UI元素&…

Figma语言设置教程:简易切换至中文,提高操作便捷性!

Figma是世界领先的在线协作UI设计工具。它摆脱了Sketch等传统设计软件对设备的依赖&#xff0c;使设计师可以随时随地使用任何设备打开网页浏览器&#xff0c;轻松实现跨平台、跨时空的设计合作。那么&#xff0c;Figma如何改变中文&#xff0c;以提高国内设计师的使用体验呢&a…

systemverilog:interface中端口方向理解

&#xff08;1&#xff09;从testbench的角度看&#xff0c;tb中信号的输入输出方向与interface中信号输入输出方向一致&#xff1a; &#xff08;2&#xff09;从DUT角度看&#xff0c;DUT中信号输入输出方向与interface中信号输入输出方向相反。简单图示如下&#xff1a; 代…