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

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

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

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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值