Python中模块和线程之间的全局变量

4

我有一个名为config.py的配置文件,其中包含一个全局变量(默认值为5),即在config.py中我有:

# config.py
globalVar = 5

现在,在一个名为run.py的模块中,我设置了全局变量,然后调用了一个打印函数:

# run.py
import config
import test
config.globalVar = 7
test.do_printing()

# test.py
import config
def do_printing():
  print(config.globalVar)

这段代码本来是可以正常工作的(即打印出 7),但如果我在 test.py 中使用多个线程进行打印,那么它就不再起作用了,也就是说,线程看不到 run.py 所做的更改(即打印出 5)。

有什么解决方法吗?


@CheynShmuel 它打印出5(默认值)。因此,我认为这些线程创建了它们自己的config.py副本。 - machinery
你什么时候运行 run.py 文件? - Cheyn Shmuel
@CheynShmuel 我已经更新了run.py文件。我首先在配置文件中设置了变量,然后调用了一个打印函数。如果只有一个线程,它会打印7(如预期)。如果我使用多个线程进行打印,则每个线程都会打印5。 - machinery
@CheynShmuel 实际上,这只是一个小例子。我正在使用RandomizedSearchCV,它会自动生成多个线程... - machinery
线程调用打印函数do_printing()... - machinery
显示剩余3条评论
1个回答

4
即使在同一个线程上运行,你可能也会遇到问题。例如,如果你执行from config import globalVar,如果你重新绑定了局部模块中的globalVar,它就会丢失对config模块中对象的引用。
即使你没有这样做,如果变量在各种模块导入时发生更改,很难跟踪实际的导入顺序。
当你添加线程时,由于各种竞争条件,这变得完全无法管理。除了竞争条件(即你的某个线程在另一个线程设置变量之前读取了该变量),或者不正确的导入之外,线程不应以你所描述的方式影响全局变量的可见性。
具有确定性代码的解决方案是使用适合在线程之间交换(和保护)数据的数据结构。 threading模块本身提供了Event对象,可以用于一个线程等待,直到另一个线程更改你所期望的值:
config.py:
changed = Event()
changed.clear()

global_var = 5

工作线程中的模块:

import config

def do_things():
    while True:
        config.changed.wait()  # blocks until other thread sets the event
        do_more_things_with(config.global_var)

在主线程上:

import config

config.global_var = 7
config.changed.set()  # FRees the waiting Thread to run

在上面的代码中,我总是使用点表示法引用 config 中的对象。对于 "event" 对象没有任何影响 - 我可以使用 from config import changed - 因为我正在处理同一对象的内部状态,它会起作用 - 但如果我使用 from config import global_var 并重新分配它与 global_var = 7,那只会改变当前模块上下文中的 local_var 名称指向的位置。 config.local_var 仍然引用原始值。
顺便说一下,值得看一下队列模块线程本地数据

当它仍然不起作用时

另一个看不到更改的可能性是,由于并行性不在您的代码中,而是在另一个库中,它使用 multiprocessing 模块生成进程而不是线程。
如果您期望使用线程并且生成的是 multiprocessing 的进程,则会出现您所描述的问题:全局变量的更改在其他进程中不可见(因为每个进程都有自己的变量,当然)。
如果是这种情况,则可以拥有跨进程同步的(数字型,类型化)对象。检查 ArrayValue 类以及 multiprocessing Queue 来发送和接收(大多数)任意对象。
(在您的代码中添加一个 import multiprocessing; print(multiprocessing.current_process()) 行以确保。无论结果如何,请建议 RandomizedSearchCV 文档维护者明确说明他们对并行性做了什么)

谢谢您的回答,但它并没有起作用...没有进展...线程似乎创建了配置的新副本,然后永远等待信号继续...顺便说一下,我正在使用RandomizedSearchCV来生成这些线程。 - machinery
我之前没有提到,但如果你的应用程序使用“多进程”而不是“线程”,那么情况就会完全不同 - 尽管从API角度来看,这两个模块是相同的。你确定该库正在生成线程而不是子进程吗? - jsbueno
从文档中并不清楚RandomizedSearchCV是否使用多进程。如何使用多进程来解决这个问题? - machinery
我已经在答案中添加了该怎么做的内容。 - jsbueno
使用锁机制解决OP的问题如何?这样可以使修改线程安全。 - variable

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接