锁定不会像我希望的那样锁定我的变量

Locking does not lock my variable as I want it to

我正在尝试使用多线程递增变量。然而,当我 运行 代码时,计数器保持在 1。当我删除第二次睡眠时,它正常工作(增加到 5),但是我似乎无法弄清楚如何正确锁定变量。

我已经尝试在创建 tmp 之前锁定变量以及其他锁定方法(使用锁定、try-finalize 等)。

class Casino:
    euro = 0


class PlayingThread(threading.Thread):

    def __init__(self, the_casino, playerno=1):
        threading.Thread.__init__(self)
        self.lock = threading.Lock()
        self.playerno = playerno
        self.the_casino = the_casino

    def run(self):
        time.sleep(2)
        tmp = self.the_casino.euro
        time.sleep(1)
        self.lock.acquire()
        self.the_casino.euro = tmp + 1
        self.lock.release()


casino = Casino()
lt = []
for i in range(0, 5):
    pt = PlayingThread(casino, i)
    pt.start()
    lt.append(pt)

for t in lt:
    t.join()

print("We earned a lot of money! Sum=", casino.euro)

预期输出为 "We earned ... Sum=5" 但它是“... Sum=1”

您的所有线程在锁定变量之前都读取变量

    tmp = self.the_casino.euro

第二次睡眠确保所有线程都有时间在任何线程更改它之前看到 self.the_casino.euro 等于零。

然后,他们醒来后,每个人都将其设置为tmp + 1(即,他们每个人都将其设置为1)。


如果你想得到5,那么你需要把读取和更新变量变成一个原子操作。您可以通过将读取和更新放在同一个关键部分来做到这一点。

试试这个

class Casino:
euro = 0


class PlayingThread(threading.Thread):

    def __init__(self, the_casino, playerno=1):
        threading.Thread.__init__(self)
        self.lock = threading.Lock()
        self.playerno = playerno
        self.the_casino = the_casino

    def run(self):
        try:
            self.lock.acquire()
            self.the_casino.euro += 1
        finally:
            self.lock.release()


casino = Casino()
lt = []
for i in range(0, 5):
    pt = PlayingThread(casino, i)
    pt.start()
    lt.append(pt)

for t in lt:
    t.join()

print("We earned a lot of money! Sum=", casino.euro)

问题是您实际上并没有递增 casino.euro,它始终为 0 并分配给 tmp。

你有两个错误。第一个是 Solomon Slow 的回答中提到的那个。在整个读取-修改-写入操作期间,您不持有锁。正如您所建议的,这可以通过向上移动锁定获取来解决。

但是你还有另一个问题——没有锁保护 euro。每个线程锁定自己,允许每个线程获得对自身的锁定并仍然同时进行读取-修改-写入。

要解决这个问题,需要有一个特定的锁来保护 euro,并且对它的所有操作都必须在那个锁的保护下完成。我为 Casino 添加了一个锁来做到这一点。

固定代码如下:

import threading
import time

class Casino:
    euro = 0
    lock = threading.Lock();


class PlayingThread(threading.Thread):

    def __init__(self, the_casino, playerno=1):
        threading.Thread.__init__(self)
        self.lock = threading.Lock()
        self.playerno = playerno
        self.the_casino = the_casino

    def run(self):
        time.sleep(2)
        self.the_casino.lock.acquire()
        tmp = self.the_casino.euro
        time.sleep(1)
        self.the_casino.euro = tmp + 1
        self.the_casino.lock.release()


casino = Casino()
lt = []
for i in range(0, 5):
    pt = PlayingThread(casino, i)
    pt.start()
    lt.append(pt)

for t in lt:
    t.join()

print("We earned a lot of money! Sum=", casino.euro)

我认为您可能遗漏了有关锁工作原理的一些基本知识。锁不知道它锁定了什么,并且除了两个线程同时持有同一个锁之外不会阻止任何事情。您要确保没有线程可以在其他线程读取 euro、增加读取值并写回它之间读取 euro。这样做的方法是确保可能以任何方式与 euro 交互的每个线程都持有一个特定的锁。

当我们说某些特定的锁保护某些特定的数据时,我们的意思是没有线程在不持有该特定锁的情况下尝试访问或修改该特定数据。显然,这需要仔细构造代码以符合此要求。