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

标签 python multithreading queue

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

然后您将使用 acquirerelease 函数围绕关键代码段。

    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 语句集成得很好。如果发生异常,这具有自动解锁的额外好处。因此,除了上面的 acquirerelease 系统,您可以这样做:

    for i in switches:
        with switch_lock:
            i.toggle()

关于python - 如何在 python 中的线程之间安全地传递变量值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25026985/

相关文章:

c - 按名称对队列进行排序不起作用

Python - 如何与系统托盘弹出窗口交互

Python,导入 pandas 时没有 'isclass' 的 AttributeError

multithreading - Clojure - 使用代理会大大减慢执行速度

Java 读取 .txt 文件并使用子字符串进行排序

php - 在 Laravel 中将事务与队列结合

python - 我可以在 Linux 上动态加载可执行文件吗?

python - 根据值的第 n 次重复创建 Pandas 列

multithreading - Intel Nehalem 单线程峰值性能

android - 使用多线程从 Internet 检索单个图像