使用 Python 线程锁来防止条件竞争

本文介绍了竞争条件以及如何使用 Python中threading模块中的Lock对象来防止竞争的出现。

什么是条件竞争

当两个线程试图同时访问一个共享变量时,就会出现条件竞争。

第一个线程从共享变量中读取值,第二个线程也从同一个共享变量中读取值。

然后两个线程都尝试更改共享变量的值,他们竞相查看哪个线程最后向变量写入值。

最后写入共享变量的线程的值被保留,因为它覆盖了前一个线程写入的值。

竞争示例条件

以下示例说明了条件竞争:

from threading import Thread
from time import sleep
counter = 0
def increase(by):
global counter
local_counter = counter
local_counter += by
sleep(0.1)
counter = local_counter
print(f'counter={counter}')
# create threads
t1 = Thread(target=increase, args=(10,))
t2 = Thread(target=increase, args=(20,))
# start the threads
t1.start()
t2.start()
# wait for the threads to complete
t1.join()
t2.join()
print(f'The final counter is {counter}')

在上面这个例子中,两个线程都试图同时修改counter变量的值,counter变量的值取决于哪个线程最后完成。

如果t1线程在t2线程之前完成,将看到以下输出:

counter=10
counter=20
The counter is 20

否则,看到以下输出:

counter=20
counter=10
The final counter is 10

使用 Lock 来防止条件竞争

为防止条件竞争,可以使用threading模块中的Lock类。锁有两种状态:锁定和解锁。

首先,创建一个Lock类的实例:

<span style="background-color:#f8f8f8"><span style="color:#212529"><code class="language-python">lock = Lock()</code></span></span>

默认情况下,锁处于解锁状态,直到获得这个锁。

其次,通过调用acquire()方法获取锁:

<span style="background-color:#f8f8f8"><span style="color:#212529"><code class="language-python">lock.acquire()</code></span></span>

第三,线程完成更改共享变量后释放锁:

<span style="background-color:#f8f8f8"><span style="color:#212529"><code class="language-python">lock.release()</code></span></span>

下面的例子展示了如何使用该Lock对象来防止前面程序中的竞争条件:

from threading import Thread, Lock
from time import sleep
counter = 0
def increase(by, lock):
global counter
lock.acquire()
local_counter = counter
local_counter += by
sleep(0.1)
counter = local_counter
print(f'counter={counter}')
lock.release()
lock = Lock()
# create threads
t1 = Thread(target=increase, args=(10, lock))
t2 = Thread(target=increase, args=(20, lock))
# start the threads
t1.start()
t2.start()
# wait for the threads to complete
t1.join()
t2.join()
print(f'The final counter is {counter}')

还可以把上面的方法改成一个Counter类。

from threading import Thread, Lock
from time import sleep
class Counter:
def __init__(self):
self.value = 0
self.lock = Lock()
def increase(self, by):
self.lock.acquire()
current_value = self.value
current_value += by
sleep(0.1)
self.value = current_value
print(f'counter={self.value}')
self.lock.release()
counter = Counter()
# create threads
t1 = Thread(target=counter.increase, args=(10, ))
t2 = Thread(target=counter.increase, args=(20, ))
# start the threads
t1.start()
t2.start()
# wait for the threads to complete
t1.join()
t2.join()
print(f'The final counter is {counter.value}')

总结

  • 当两个线程同时访问共享变量时,就会出现条件竞争。
  • 使用Lock对象来防止条件竞争。
  • 调用Lock对象的acquire()方法获取锁。
  • 调用Lock对象的release()方法释放之前获取的锁。

猜你喜欢

转载自blog.csdn.net/qq_41221596/article/details/131616883
今日推荐