【问题标题】:Why does python require you to acquire a lock before waiting on a condition为什么python要求您在等待条件之前获取锁
【发布时间】:2019-07-07 06:22:59
【问题描述】:

Python 有一个名为Condition 的线程对象,它会阻塞一个线程,直到另一个线程调用notifiy()notify_all()。但是,在调用wait() 方法之前,您必须先调用acquire() 来获取内部锁。 wait() 方法然后释放锁并等待通知,之后它将继续重新获取锁,您可以运行一些需要线程安全的代码。我的问题是,当您调用 wait() 方法时,为什么 Condition 对象不会在内部自动获取锁:

Python 线程文档

必须在持有相关锁的情况下调用其他方法。 wait() 方法释放锁,然后阻塞直到另一个线程通过调用notify()notify_all() 唤醒它。一旦被唤醒,wait() 重新获取锁并返回。也可以指定超时。

所以在这段代码中我获取了锁,wait 方法立即释放它,然后在收到通知后它再次获取它,然后我最终释放它。

lock = threading.Lock()
condition = threading.Condition(lock=lock)
...
condition.lock()    # acquire the lock
condition.wait()    #  waiting for another thread to notify me
condition.release() # release the lock

为什么wait() 调用不只是等待然后在收到通知后获取锁我不明白为什么我要获取它将释放的锁

【问题讨论】:

  • 我没有答案,但我很高兴看到其他人有答案。这是一个很好的问题。 :)

标签: python multithreading python-internals


【解决方案1】:

如果您没有持有锁,您正在等待的事情可能会在您开始等待之前发生。

假设您有一个自制消息队列,它使用 collections.dequethreading.Lockthreading.Condition 变量实现。

线程 A 想从队列中读取一个项目,所以它抓取lock,检查deque,没有项目。线程 A 调用condition.wait 等待另一个线程放入。

线程 B 获取lock,调用deque.append,并调用condition.notify。由于condition.notify 调用,线程 A 现在计划唤醒。

想象一下,如果线程 A 可以在调用 condition.wait 之前释放 lock。在这种情况下,线程 B 可能会在线程 A 开始等待之前附加其项目并调用condition.notify。线程 A 永远不会醒来。

【讨论】:

  • 很好的答案。只需添加一个注释:这种“永远等待”操作仍然可以通过锁守卫发生——它只是确保在lockwait 之间发生的操作不会导致执行线程丢失消息。如果线程 B 在线程 A 到达 lock 调用之前更新了一条消息,那么线程 A 仍可能无限期地等待。
  • @flakes:如果线程 B 在线程 A 获取锁之前将消息放入,那么线程 A 会看到消息而不等待。当然,如果你把事情搞砸并制造了一个错误,仍然有可能无限等待。
  • "如果线程 B 在线程 A 获取锁之前将消息放入,那么线程 A 会看到消息而不等待" 这不是我的理解。 notify 只会在线程已经获得 lock 并且正在等待时才会唤醒它们。如果您在前一个 notify 之后锁定并等待,您将阻塞线程,直到进行下一个 notify 调用。这是确保的,因为通知线程也必须获取相同的锁。
  • @flakes: 但是线程 A 会抓住锁然后等待,因为它会看到元素线程 B 放入。它甚至不会调用 wait,所以不需要唤醒 A。如果线程 A 无条件等待,那将是线程 A 代码中的错误。
  • 啊,您专门针对您的双端队列示例而不是一般情况? (即消息不是作为通知调用,而是作为容器中的值)。我相信我们的协议很激烈。
猜你喜欢
  • 2015-12-22
  • 1970-01-01
  • 2021-07-23
  • 1970-01-01
  • 1970-01-01
  • 2019-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多