我在 python 文档中读到 Queue.Queue() 是一种在不同线程之间传递变量的安全方法。我真的不知道多线程存在安全问题。对于我的应用程序,我需要开发多个对象,这些对象具有可以从多个不同线程访问的变量。现在我只是让线程直接访问对象变量。我不会在这里展示我的代码,因为它太多了,但这里有一个例子来演示我在做什么。
from threading import Thread
import time
import random
class switch:
def __init__(self,id):
self.id=id
self.is_on = False
def self.toggle():
self.is_on = not self.is_on
switches = []
for i in range(5):
switches[i] = switch(i)
def record_switch():
switch_record = {}
while True:
time.sleep(10)
current = {}
current['time'] = time.srftime(time.time())
for i in switches:
current[i.id] = i.is_on
switch_record.update(current)
def toggle_switch():
while True:
time.sleep(random.random()*100)
for i in switches:
i.toggle()
toggle = Thread(target=toggle_switch(), args = ())
record = Thread(target=record_switch(), args = ())
toggle.start()
record.start()
据我了解,队列对象只能用于放置和获取值,这显然对我不起作用。我这里的东西“安全”吗?如果没有,我该如何编程才能安全地从多个不同线程访问变量?
最佳答案
每当您有线程修改其他线程可以看到的值时,您就会遇到安全问题。担心的是,当另一个线程正在修改一个值时,一个线程会尝试修改它,这具有风险和未定义的行为。所以不,您的开关切换代码不安全。
需要了解的重要一点是,更改变量的值并不能保证是原子的。如果一个 Action 是原子的,这意味着 Action 将始终在一个不间断的步骤中发生。 (这与数据库定义略有不同。)更改变量值,尤其是列表值,通常会在处理器级别执行多个步骤。当您使用线程时,在另一个线程开始工作之前,不能保证所有这些步骤同时发生。当线程 B 突然接管时,线程 A 完全有可能在更改变量 x
的过程中进行到一半。然后,如果线程 B 尝试读取变量 x
,它不会找到正确的值。更糟糕的是,如果线程 B 试图修改变量 x
而线程 A 正在做同样的事情,那么可能会发生不好的事情。 只要您有一个值可以以某种方式改变的变量,对它的所有访问都需要是线程安全的。
如果您要修改变量而不是传递消息,您应该使用 Lock
对象。
在你的情况下,你会在顶部有一个全局 Lock
对象:
from threading import Lock
switch_lock = Lock()
然后您将使用 acquire
和 release
函数围绕关键代码段。
for i in switches:
switch_lock.acquire()
current[i.id] = i.is_on
switch_lock.release()
for i in switches:
switch_lock.acquire()
i.toggle()
switch_lock.release()
一次只能有一个线程获取锁(无论如何都是这种锁)。当任何其他线程尝试时,它们将被阻塞并等待锁再次释放。因此,通过对代码的关键部分加锁,可以防止多个线程在任何时候查看或修改给定的开关。您可以将它放在任何您希望一次独占一个线程的代码周围。
编辑:正如 martineau 指出的那样,如果您使用的是具有锁的 Python 版本,则锁与 with
语句集成得很好。如果发生异常,这具有自动解锁的额外好处。因此,除了上面的 acquire
和 release
系统,您可以这样做:
for i in switches:
with switch_lock:
i.toggle()
关于python - 如何在 python 中的线程之间安全地传递变量值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25026985/