代码之家  ›  专栏  ›  技术社区  ›  MirceaKitsune

使池中的线程注意全局变量的更改

  •  0
  • MirceaKitsune  · 技术社区  · 1 年前

    我遇到了一个有趣的游泳池环境,但我并不完全理解。我知道,如果您编辑线程中的任何变量或对象,更改将不会应用于主线程,而只存在于该工作线程的孤立现实中。然而,现在我注意到,池中的线程甚至不会检测到主线程对全局变量所做的更改,如果这些更改是在池启动之后甚至之前进行的。

    import multiprocessing as mp
    
    variable = 0
    
    def double(i):
        return i * variable
    
    def main():
        pool = mp.Pool()
        for result in pool.map(double, [1, 2, 3]):
            print(result)
        variable = 1
    
    main()
    

    显然,为了简化示例,在我的情况下,我需要线程来查看由作为对象属性的主循环修改的列表内容的更新。有趣的是,即使我动了 variable = 1 之前 pool = mp.Pool() 在我的测试中,线程总是看到0,从来没有注意到变量变为1。

    使用对象时的作用是更改与线程相关联的对象上的变量。然后发生的奇怪的事情是,主线程的性能显著下降,因为它每次调用都要使用更多的CPU:就好像仅仅通知线程池列表的更改就增加了大量的工作量。

    让线程池看到由主线程修改的全局变量或对象变量的更改的最有效和最便宜的方法是什么 pool.map_async pool.apply_async 线程使用该var的更新版本?

    1 回复  |  直到 1 年前
        1
  •  1
  •   freakish    1 年前

    有趣的是,即使我动了 variable = 1 之前 pool = mp.Pool() 在我的测试中,线程总是看到0,从来没有注意到变量变为1。

    首先,你需要申报 global variable 在里面 main 。否则Python会认为这是一个局部变量。

    但即使你这样做,也不会有太大改变。那是因为 multiprocessing 包派生(顾名思义)进程。不是线程。进程与线程相似。主要区别在于每个进程都有独立的内存。这意味着一个进程永远看不到其他进程的内存。

    除非您实际使用专门为 inter-process communication .Python太好了,它会为你包装其中的一些。特别是,您可以从中发送和检索数据 pool.map 。只需传递一个参数列表,然后检索结果。

    然而,这既不便宜也不高效。至少与简单的内存操作相比。Python的多处理通信是在管道之上实现的。这种通信需要双方的对象序列化和反序列化。它很重。因此,您应该避免发送和检索大对象。事情就是这样。

    另一种选择是使用 multiprocessing.Value 和/或 multiprocessing.Array 。我不太确定这些是如何实现的,可能是共享内存和锁的某种组合。这可能比以前的方法更有效,但它有自己的局限性。