繁体   English   中英

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

[英]how to safely pass a variables value between threads in python

我在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在线程A进行相同操作的一半时尝试修改变量x则可能会发生不良情况。 只要您有一个变量的值可以以某种方式更改,就需要对它的所有访问都是线程安全的。

如果要修改变量而不是传递消息,则应使用Lock对象。

就您而言,您将在顶部拥有一个全局Lock对象:

from threading import Lock

switch_lock = Lock()

然后,您将使用acquirerelease功能来acquire关键代码。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM