在python中如何在线程之间安全地传递变量值

在python中如何在线程之间安全地传递变量值,python,multithreading,queue,Python,Multithreading,Queue,我在python文档中读到Queue.Queue()是在不同线程之间传递变量的安全方法。我真的不知道多线程存在安全问题。对于我的应用程序,我需要使用可以从多个不同线程访问的变量开发多个对象。现在我只是让线程直接访问对象变量。我不会在这里显示我的代码,因为代码太多了,但这里有一个示例来演示我在做什么 from threading import Thread import time import random class switch: def __init__(self,id):

我在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()

因此,据我所知,队列对象只能用于放置和获取值,这显然对我不起作用。我这里的东西安全吗?如果没有,我如何对其进行编程,以便可以安全地从多个不同的线程访问变量?

每当有线程修改其他线程可以看到的值时,就会出现安全问题。令人担心的是,当另一个线程处于修改它的中间时,线程将尝试修改一个值,该线程具有风险和未定义的行为。所以不,你的开关切换代码是不安全的

要知道的重要一点是,更改变量的值不能保证是原子的。如果一个动作是原子的,这意味着动作总是在一个不间断的步骤中发生。(这与数据库定义略有不同。)更改变量值,尤其是列表值,通常需要在处理器级别执行多个步骤。当您使用线程时,不能保证在另一个线程开始工作之前,所有这些步骤都会同时发生。当线程B突然接管时,线程A完全有可能在更改变量
x
的中途完成。然后,如果线程B尝试读取变量
x
,它将找不到正确的值。更糟糕的是,如果线程B试图修改变量
x
,而线程A正在做同样的事情,那么坏事情可能会发生每当您有一个变量的值可以以某种方式更改时,对它的所有访问都必须是线程安全的。

如果要修改变量而不是传递消息,则应使用AOObject

在您的情况下,顶部会有一个全局
对象:

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()

每当有线程修改其他线程可以看到的值时,就会出现安全问题。令人担心的是,当另一个线程处于修改它的中间时,线程将尝试修改一个值,该线程具有风险和未定义的行为。所以不,你的开关切换代码是不安全的

要知道的重要一点是,更改变量的值不能保证是原子的。如果一个动作是原子的,这意味着动作总是在一个不间断的步骤中发生。(这与数据库定义略有不同。)更改变量值,尤其是列表值,通常需要在处理器级别执行多个步骤。当您使用线程时,不能保证在另一个线程开始工作之前,所有这些步骤都会同时发生。当线程B突然接管时,线程A完全有可能在更改变量
x
的中途完成。然后,如果线程B尝试读取变量
x
,它将找不到正确的值。更糟糕的是,如果线程B试图修改变量
x
,而线程A正在做同样的事情,那么坏事情可能会发生每当您有一个变量的值可以以某种方式更改时,对它的所有访问都必须是线程安全的。

如果要修改变量而不是传递消息,则应使用AOObject

在您的情况下,顶部会有一个全局
对象:

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()

如果
开关
类仅用于多线程设置,则将锁集成到对象中,并将锁逻辑放入
切换
本身可能是有意义的。读取一个
bool
总是原子化的,因此您不需要在写入程序和读取程序之间进行同步,除非交换机中的数据变得更复杂(多个字段都可以被同步)