Skip to main content
 首页 » 编程设计

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

2025年05月04日14duanxz

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