递归锁与普通锁的区别

什么是锁?

在多线程编程中,锁是一种机制,用来确保某些代码块在同一时间只能被一个线程执行。想象一下,你和你的朋友们都想同时进入一个只有一把椅子的房间。为了避免混乱,你们需要一个锁来控制进入的顺序。

普通锁(Lock)

普通锁就像是一个简单的门锁。你拿到钥匙(获取锁),进了房间(执行代码),然后记得把钥匙放回去(释放锁),这样别人才能进来。

让我们看看Python中的普通锁是如何工作的:

import threading

lock = threading.Lock()

def critical_section():
    with lock:
        print(f"{threading.current_thread().name} has entered the critical section")
        # 模拟一些工作
        import time
        time.sleep(1)
        print(f"{threading.current_thread().name} is leaving the critical section")

threads = []
for i in range(3):
    thread = threading.Thread(target=critical_section)
    threads.append(thread)
    thread.start()

for thread in threads:
    thread.join()

在这个例子中,我们创建了一个普通锁,并在critical_section函数中使用它。每个线程在进入关键区之前都会获取锁,并在离开时释放锁。这样可以确保同一时间只有一个线程在执行关键区的代码。

普通锁的死锁场景
普通锁虽然简单高效,但在某些情况下会导致死锁。让我们看看一个死锁的例子:

import threading

lock = threading.Lock()

def deadlock_function():
    lock.acquire()
    print(f"{threading.current_thread().name} has acquired the lock")
    # 尝试再次获取同一把锁,导致死锁
    lock.acquire()
    print(f"{threading.current_thread().name} has acquired the lock again")
    lock.release()
    lock.release()

thread = threading.Thread(target=deadlock_function)
thread.start()
thread.join()

在这个例子中,deadlock_function函数尝试两次获取同一把锁。第一次获取锁成功,但第二次获取锁时,由于锁已经被当前线程持有,导致死锁。程序会卡在第二次获取锁的地方,无法继续执行。

递归锁(Reentrant Lock)

递归锁就像是一个聪明的门锁,它知道你已经在房间里了,所以如果你再试图进入,它会让你进去,而不会把你锁在外面。这在递归函数或需要多次获取同一把锁的情况下特别有用。

让我们看看递归锁是如何工作的:

import threading

rlock = threading.RLock()

def recursive_function(level):
    with rlock:
        print(f"{threading.current_thread().name} has entered level {level}")
        if level > 0:
            recursive_function(level - 1)
        print(f"{threading.current_thread().name} is leaving level {level}")

thread = threading.Thread(target=recursive_function, args=(3,))
thread.start()
thread.join()

在这个例子中,我们使用了递归锁RLock。recursive_function函数会递归调用自己,并在每个递归层级获取同一把锁。递归锁允许同一个线程多次获取锁,而不会导致死锁。

递归锁 vs 普通锁

  • 普通锁:简单高效,但同一个线程不能多次获取同一把锁,否则会导致死锁。
  • 递归锁:允许同一个线程多次获取同一把锁,适用于递归调用或需要多次获取锁的情况,但开销稍大。

更清晰的案例

递归锁:

import threading

recursive_lock = threading.RLock()

def recursive_function(n):
    if n <= 0:
        return
    recursive_lock.acquire()
    print(f"Acquired lock, n={n}")
    recursive_function(n-1)
    recursive_lock.release()
    print(f"Released lock, n={n}")

recursive_function(3)

输出:
Acquired lock, n=3
Acquired lock, n=2
Acquired lock, n=1
Released lock, n=1
Released lock, n=2
Released lock, n=3

普通锁:

import threading

recursive_lock = threading.Lock()

def recursive_function(n):
    if n <= 0:
        return
    recursive_lock.acquire()
    print(f"Acquired lock, n={n}")
    recursive_function(n-1)
    recursive_lock.release()
    print(f"Released lock, n={n}")

recursive_function(3)

运行一天的输出:
Acquired lock, n=3

结论:

锁在多线程编程中是必不可少的工具。普通锁适用于简单的同步场景,而递归锁则在复杂的递归或多次锁定场景中大显身手。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 答:pthread_mutex_init有三种互斥:PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK和PTHREAD_MUTEX_RECURSIVE。PTHREAD_MUTEX_NORMAL是最基本的互斥,它只支持加和解操作;PTHREAD_MUTEX_ERRORCHECK可以检测死和重复加;PTHREAD_MUTEX_RECURSIVE可以支持多次加,解也需要多次才能完全解开。 ### 回答2: pthread_mutex_init函数是用于初始化互斥的函数,它可以创建三种不同类型的互斥,分别是普通(PTHREAD_MUTEX_NORMAL)、错误检查(PTHREAD_MUTEX_ERRORCHECK)和递归(PTHREAD_MUTEX_RECURSIVE),它们之间的区别如下: 1. 普通(PTHREAD_MUTEX_NORMAL): 普通是最基本的互斥类型,它没有提供死检测功能。如果同一个线程对已经加的互斥再次调用pthread_mutex_lock函数,就会出现死情况,进程可能会陷入无法继续执行的状态。因此,在使用普通时需要特别注意避免死。 2. 错误检查(PTHREAD_MUTEX_ERRORCHECK): 错误检查是一种提供了死检测功能的互斥类型。当同一个线程对已经加的错误检查再次调用pthread_mutex_lock函数时,线程不会进入死状态,而是会返回一个错误码,通过这个错误码可以判断是否出现死。这种互斥类型增加了线程之间的安全性,但会带来些许额外的开销。 3. 递归(PTHREAD_MUTEX_RECURSIVE): 递归是允许同一个线程多次对互斥进行加操作的类型。在同一个线程内部,可以对已经加的互斥再次加,而不会造成死。需要注意的是,每次对递归进行加操作时,都必须相应的进行解操作,否则其他线程无法对该进行加递归常用于复杂的程序设计中,可以简化处理复杂情况时的加操作。 以上是三种不同类型的互斥区别。根据实际需求,选择合适的互斥类型可以有效地保证线程的安全性和程序的正常运行。 ### 回答3: pthread_mutex_init函数是用来初始化互斥的,它提供了三种不同类型的互斥,分别是普通(PTHREAD_MUTEX_NORMAL)、递归(PTHREAD_MUTEX_RECURSIVE)和错误检查(PTHREAD_MUTEX_ERRORCHECK)。这三种在使用上有一些区别。 1. 普通(PTHREAD_MUTEX_NORMAL): 普通是最简单的一种互斥类型。当一个线程占用了该后,其他线程将被阻塞。如果同一个线程再次请求这个,就会产生死。因此,对于普通,需要确保的获取和释放是成对的且不会有嵌套的请求。 2. 递归(PTHREAD_MUTEX_RECURSIVE): 递归可以被同一个线程多次获取,并能够正常释放。这意味着同一个线程在持有的同时可以多次请求该,而不会产生死递归使用一个计数器来跟踪的持有次数,每次请求时计数器加1,每次释放时计数器减1。只有当计数器为0时,才能被其他线程获取。 3. 错误检查(PTHREAD_MUTEX_ERRORCHECK): 错误检查会在每次对的操作(获取、释放)之前进行错误检查,以确保的使用是正确的。如果发生了错误的使用,比如同一个线程重复获取,或者释放未持有的,将会返回一个错误码。错误检查在性能上可能会存在一定的开销,因此在性能要求较高的场景下可能不太适用。 一般来说,递归比较适用于同一个线程需要多次获取的情况,而普通适用于不需要嵌套请求的情况。错误检查则是为了更加严格地检查的正确使用情况。根据具体的使用需求,可以选择适合的互斥类型来保证线程安全。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值