【问题标题】:Locking does not lock my variable as I want it to锁定不会像我想要的那样锁定我的变量
【发布时间】:2019-03-24 16:01:36
【问题描述】:

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

我已经尝试在创建 tmp 之前锁定变量以及其他锁定方法(使用 lock、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)

预期的输出是“我们赚了 ... Sum=5”,但它是“... Sum=1”

【问题讨论】:

  • 当你说“锁定变量”时,你指的是什么变量?如果您的意思是euro,是什么让您认为您的线程锁定euro?如果你指的是其他变量,那是什么变量?

标签: python multithreading locking


【解决方案1】:

你有两个错误。第一个是 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 交互的每个线程都持有一个特定的锁。

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

【讨论】:

    【解决方案2】:

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

        tmp = self.the_casino.euro
    

    第二次休眠确保所有线程有时间看到self.the_casino.euro 等于零,然后它们中的任何一个更改它。

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


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

    【讨论】:

    • 是的,这就是我尝试在读取之前放置锁的原因。但这没有帮助。锁不妨碍阅读吗?
    • @wieli99 Lock 对象不知道 Casino 对象。锁定一个锁只能防止其他线程同时锁定同一个Lock。它不保护任何变量。如果您希望变量受到保护,那么您需要确保您的线程仅在持有锁时才能访问它们。
    • 对不起,如果我在这里很愚蠢,但不应该'获取->读取->写入->释放'修复它吗?
    • 我无法评论我没见过的代码。我的回答解释了为什么我对您向我们展示的程序打印 ...Sum=1 并不感到惊讶。
    • 代码将保持不变,我只需将 lock.acquire() 拉高两行 - 在 sleep(2) 之后 - 我不明白为什么不能解决它。
    【解决方案3】:

    试试这个

    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。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-06
      • 1970-01-01
      • 2021-12-25
      • 2012-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-20
      相关资源
      最近更新 更多