深入理解Python中的threading.Lock同步锁与无锁机制在多线程中的应用

文章目录

深入理解Python中的threading.Lock同步锁与无锁机制在多线程中的应用

概念

螺纹锁定锁可用于确保多个线程对共享数据的独家访问。

当线程获取锁定时,其他线程将无法在此期间再次获取锁,直到线释放锁为止。这样可以确保对共享数据的独家访问,从而避免了数据不一致的问题;但是,锁的使用不正确也会导致僵局等待;

无锁

当使用多线程访问和修改公共资源时,无锁定会导致数据与预期结果不同;

import threading
# 创建一个Lock对象
lock = threading.Lock()
# 共享资源
counter = 0
# 线程函数
def worker():
    global counter
    # 获取锁
        # 对共享资源进行操作
    for i in range(1000000):
        counter += 1
    print("Counter value: ", counter)
# 创建两个线程并启动它们
t1 = threading.Thread(target=worker)
t2 = threading.Thread(target=worker)
t1.start()
t2.start()
t1.join()
t2.join()
########################  结果  ############################
Counter value:  1264715
Counter value:  1326630
Process finished with exit code 0
############################################################

要找出原因,我们可以假设:

当线程T1计数器= 100时,+1还为时不晚;目前,线程T2还可以计数器= 100。现在,当T1和T2向计数器加1时,计数器值将变为101并将其返回到全局变量。我们发现,当两个线程同时使用和修改相同的全局变量计数器时,总数会逐一增加。因为当一个线程读取并写入公共资源时,其他线程也可以读写并写入它,并且两个线程获得相同的值,从而导致最终结果数据中产生的结果不一致。

为了防止此问题,我们必须在使用公共资源时使用锁定保护;

添加锁

import threading
# 创建一个Lock对象
lock = threading.Lock()
# 共享资源
counter = 0
# 线程函数
def worker():
    global counter
    # 获取锁
    lock.acquire()
    try:
        # 对共享资源进行操作
        for i in range(1000000):
            counter += 1
        print("Counter value: ", counter)
    finally:
        # 释放锁
        lock.release()
# 创建两个线程并启动它们
t1 = threading.Thread(target=worker)
t2 = threading.Thread(target=worker)
t1.start()
t2.start()
t1.join()
t2.join()
########################  结果  #############################
Counter value:  1000000
Counter value:  2000000
Process finished with exit code 0
############################################################

由于锁定,这两个线程成功地增加了计数器。

要找出原因,我们可以假设:

当Thread T1首先获取锁定锁并锁定数据处理部分时,假设T1获得Counter = 1;目前,Thread T2还希望获得反击,但是由于无法获得锁定锁,因此只能等待锁定锁定。当线程T1完成增量操作时,锁定锁定;此时,T2突然发现它可以获取锁定锁,因此它锁定了T2的数据处理部分,然后执行数据增量逻辑。在T2中完全执行增量操作之前,锁定锁定为锁。我们发现,由于锁定,这两个线程只能顺序获得所需的公共资源计数器,从而确保在线程流执行中使用公共资源的排他性。 。

由于锁定和没有竞争发生,因此结果是准确的,并且与期望一致。

但与此同时,多线程中错误的锁定顺序可能导致锁定等待,这通常称为“僵局”。

僵局

僵局是指两个或多个线程或过程被困在无限状态的情况下,彼此等待释放资源,并且无法继续在多线程或分布式系统中执行。

当多个线程或流程相互竞争相同的资源集时,如果每个线程拥有一些资源并正在等待另一个线程发布所需的资源,则会发生僵局。在这种情况下,所有线程都被阻止并且无法继续执行,从而导致系统中的僵尸状态。

import threading
# 创建两个Lock对象
lock1 = threading.Lock()
lock2 = threading.Lock()
# 线程函数1
def worker1():
    lock1.acquire()
    print("Worker 1 acquired lock 1")
    lock2.acquire()
    print("Worker 1 acquired lock 2")
    lock2.release()
    print("Worker 1 released lock 2")
    lock1.release()
    print("Worker 1 released lock 1")
# 线程函数2
def worker2():
    lock2.acquire()
    print("Worker 2 acquired lock 2")
    lock1.acquire()
    print("Worker 2 acquired lock 1")
    lock1.release()
    print("Worker 2 released lock 1")
    lock2.release()
    print("Worker 2 released lock 2")
# 创建两个线程并启动它们
t1 = threading.Thread(target=worker1)
t2 = threading.Thread(target=worker2)
t1.start()
t2.start()
t1.join()
t2.join()
########################  结果  ############################
Worker 1 acquired lock 1
Worker 2 acquired lock 2
...无尽的等待
############################################################

在上面的代码中,我们创建两个锁定对象,并在两个线程中使用不同的订单来获取两个锁。

线程1首先获得锁定1,然后尝试获得锁2;当Thread 2首先获得Lock2,然后尝试获得Lock1。

由于这两个线程以不同的顺序获取锁,因此它们[可能]在某个时刻可以握住一个锁,但是在尝试获取另一个锁定时将被阻止。

例如,当线程1获取Lock1和Thread 2获取Lock2时,它们都在等待另一个锁定释放,从而导致僵局。

目前,该程序将不再继续执行,而是处于等待状态。

如果您想在这里避免僵局,则需要确保不同线程获取锁的顺序是一致的。

也就是说,如果线程首先获得锁定,则在尝试获取lock2而不是相反的情况下,它也应以相同的顺序获得。

解决僵局

僵局的原因通常是由竞争共享资源竞争的多个线程引起的,而没有良好的资源分配策略或锁定收购订单。在设计多线程或分布式系统时,避免僵局是一个重要的问题。避免僵局的常用方法包括:

锁定订单的标准化资源分配策略的优化使用超时等待和其他机制。

你可能想看:
« 上一篇
下一篇 »

相关推荐

python爬虫教程PDF,Python爬虫书PDF

2026年09月02日

31阅读

外国教程python,python编程教学视频

2026年09月02日

25阅读

python简明教程沈洁元,python详细基础教程

2026年09月02日

21阅读

python库包教程,python 包

2026年09月02日

27阅读

python教程大全集,python教程600集

2026年09月02日

26阅读

python入门教程完整版,python零基础入门全套教程视频

2026年09月02日

22阅读