Python线程与全局变量

-1 投票
2 回答
23455 浏览
提问于 2025-04-16 10:18

我在写Python的多线程代码时遇到了一个问题。我写了一些工作线程类,它们都导入了一个全局文件,比如sharevar.py。我需要一个变量,比如regdevid,用来跟踪注册的设备ID。当一个线程改变这个变量的值时,其他线程应该能获取到最新的值。但是结果是:当一个线程改变了这个值,其他线程仍然获取到的是我在sharevar.py文件中定义的默认值,为什么会这样呢?是我哪里出错了吗?

# thread a
from UserShare import RegDevID
import threading
class AddPosClass(threading.Thread):
global commands
# We need a pubic sock, list to store the request
def __init__(self, queue):
    threading.Thread.__init__(self)
    self.queue = queue

def run(self):
    while True:
        data = self.queue.get()
        #print data
        RegDevID = data
        #print data
        send_queue.put(data)
        self.queue.task_done()
# thread b
import threading
from ShareVar import send_queue, RegDevID 
"""
AddPos -- add pos info on the tail of the reply
"""
class GetPosClass(threading.Thread):
    global commands
    # We need a pubic sock, list to store the request
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue

    def run(self):
        while True:
            data = self.queue.get()
            #print data
            data = RegDevID
            #print data
            send_queue.put(data)
            self.queue.task_done()
# ShareVar.py
RegDevID = '100'

就是这样,当线程a改变了RegDevID,线程b仍然获取到的是它的默认值。提前谢谢你。

    from ShareVar import RegDevID

class Test():
    def __init__(self):
        pass
    def SetVar(self):
        RegDevID = 999
    def GetVar(self):
        print RegDevID

if __name__ == '__main__':
    test = Test();
    test.SetVar()
    test.GetVar()

ShareVar.py文件:

RegDevID = 100

结果:

100

为什么会这样呢?

2 个回答

6

我猜你是在没有使用lock的情况下尝试访问共享变量。如果你不先获取锁,就在一个线程中读取共享变量,而另一个线程正在写入这个变量,那么你读取到的值可能是不确定的。

解决这个问题的方法是,在读取或写入共享变量之前,确保在线程中获取锁。

import threading

# shared lock: define outside threading class
lock = threading.RLock()
# inside threading classes...
# write with lock
with lock: #(python 2.5+)
    shared_var += 1
# or read with lock
with lock:
    print shared_var

可以了解一下Python 线程处理

关于你提到的作用域问题:

在你的示例中,你遇到了作用域的问题。在SetVar()函数中,你创建了一个名为RegDevID的局部变量。而在GetVar()中,你试图读取这个RegDevID,但它并没有被定义。所以,程序会向上查找作用域,找到一个在导入中定义的同名变量。如果你希望它们引用相同的数据,变量必须在同一个作用域内。

虽然作用域是静态确定的,但在执行时是动态使用的。执行过程中,至少有三个嵌套的作用域,它们的命名空间是可以直接访问的:

最内层的作用域,首先被搜索,包含局部名称;

任何封闭函数的作用域,从最近的封闭作用域开始搜索,包含非局部但也非全局的名称;

倒数第二个作用域包含当前模块的全局名称;

最外层的作用域(最后被搜索)是包含内置名称的命名空间。

如果一个名称被声明为全局的,那么所有的引用和赋值都会直接作用于包含模块全局名称的中间作用域。否则,最内层作用域外找到的所有变量都是只读的(尝试写入这样的变量会在最内层作用域中创建一个新的局部变量,而不会改变外层同名变量的值)。

了解作用域

3

你确定你发的代码是正确的吗?你从两个不同的模块里导入了RegDevID:

# thread a
from UserShare import RegDevID

# thread b
from ShareVar import send_queue, RegDevID 

无论如何,你的问题和线程没有关系。可以把'from somemodule import somevar'想象成一个赋值语句。大致上就像是一些魔法,先检查模块是否已经加载,如果没有就加载它,然后:

somevar = sys.modules['somemodule'].somevar

当你从另一个模块导入RegDevID时,你在当前模块里创建了一个新的名字。如果你改变了这个对象,那么其他使用这个对象的人会看到这些变化,但如果你在这个模块里重新绑定这个名字,那只会影响到本地的名字,并不会改变原始模块里的内容。

相反,你需要在另一个模块里重新绑定这个变量:

import ShareVar
...
ShareVar.RegDevID = data

当然,如果你创建一个类来管理你的共享状态,你会发现事情会顺利得多。

你第二段代码的问题在于误解了局部变量和全局变量:

def SetVar(self):
    RegDevID = 999

在函数内部,你创建了一个新的局部变量RegDevID,这个变量和全局变量同名,但没有关系。如果你想重新绑定一个全局变量,可以使用global语句:

def SetVar(self):
    global RegDevID
    RegDevID = 999

撰写回答