在线程中使用全局变量

2025-03-19 08:57:00
admin
原创
54
摘要:问题描述:如何与线程共享全局变量?我的 Python 代码示例是:from threading import Thread import time a = 0 #global variable def thread1(threadname): #read variable "a"...

问题描述:

如何与线程共享全局变量?

我的 Python 代码示例是:

from threading import Thread
import time
a = 0  #global variable

def thread1(threadname):
    #read variable "a" modify by thread 2

def thread2(threadname):
    while 1:
        a += 1
        time.sleep(1)

thread1 = Thread( target=thread1, args=("Thread-1", ) )
thread2 = Thread( target=thread2, args=("Thread-2", ) )

thread1.join()
thread2.join()

我不知道如何让两个线程共享一个变量。


解决方案 1:

您只需将其声明a为全局变量thread2,这样您就不会修改a该函数的本地变量。

def thread2(threadname):
    global a
    while True:
        a += 1
        time.sleep(1)

在 中thread1,您不需要做任何特殊的事情,只要您不尝试修改 的值a(这将创建一个遮蔽全局变量的局部变量;global a如果需要请使用)>

def thread1(threadname):
    #global a       # Optional if you treat a as read-only
    while a < 10:
        print a

解决方案 2:

在函数中:

a += 1

将被编译器解释为assign to a => Create local variable a,这不是您想要的。它可能会失败并出现a not initialized错误,因为(本地) a 确实尚未初始化:

>>> a = 1
>>> def f():
...     a += 1
... 
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
UnboundLocalError: local variable 'a' referenced before assignment

您可以使用(非常不赞成的,但有充分理由的)global关键字来获得您想要的结果,如下所示:

>>> def f():
...     global a
...     a += 1
... 
>>> a
1
>>> f()
>>> a
2

但是一般来说,你应该避免使用全局变量,因为这些变量很快就会变得无法控制。对于多线程程序来说尤其如此,因为在多线程程序中,你没有任何同步机制来让你thread1知道什么时候a被修改了。简而言之:线程很复杂,当两个(或更多)线程处理同一个值时,你不能指望直观地了解事件发生的顺序。语言、编译器、操作系统、处理器……都可以发挥作用,并决定出于速度、实用性或任何其他原因修改操作顺序。

解决这种问题正确的方法是使用 Python 共享工具(锁
和朋友),或者更好的是,通过队列传递数据而不是共享它,例如像这样:

from threading import Thread
from queue import Queue
import time

def thread1(threadname, q):
    #read variable "a" modify by thread 2
    while True:
        a = q.get()
        if a is None: return # Poison pill
        print a

def thread2(threadname, q):
    a = 0
    for _ in xrange(10):
        a += 1
        q.put(a)
        time.sleep(1)
    q.put(None) # Poison pill

queue = Queue()
thread1 = Thread( target=thread1, args=("Thread-1", queue) )
thread2 = Thread( target=thread2, args=("Thread-2", queue) )

thread1.start()
thread2.start()
thread1.join()
thread2.join()

解决方案 3:

应考虑使用锁,例如threading.Lock。有关详细信息,请参阅锁对象。

接受的答案可以通过线程 1 打印 10,这不是您想要的。您可以运行以下代码以更轻松地理解该错误。

def thread1(threadname):
    while True:
      if a % 2 and not a % 2:
          print "unreachable."

def thread2(threadname):
    global a
    while True:
        a += 1

使用锁可以禁止a在读取多次时进行更改:

def thread1(threadname):
    while True:
      lock_a.acquire()
      if a % 2 and not a % 2:
          print "unreachable."
      lock_a.release()

def thread2(threadname):
    global a
    while True:
        lock_a.acquire()
        a += 1
        lock_a.release()

如果线程长时间使用该变量,那么先将其复制到局部变量是一个不错的选择。

解决方案 4:

非常感谢 Jason Pan 提出这个方法。线程 1 的 if 语句不是原子的,因此当该语句执行时,线程 2 可能会侵入线程 1,从而允许访问不可访问的代码。我将之前帖子中的想法整理成一个完整的演示程序(如下),我用 Python 2.7 运行了该程序。

通过一些深思熟虑的分析,我相信我们可以获得进一步的见解,但现在我认为重要的是展示非原子行为遇到线程时会发生什么。

# ThreadTest01.py - Demonstrates that if non-atomic actions on
# global variables are protected, task can intrude on each other.
from threading import Thread
import time

# global variable
a = 0; NN = 100

def thread1(threadname):
    while True:
      if a % 2 and not a % 2:
          print("unreachable.")
    # end of thread1

def thread2(threadname):
    global a
    for _ in range(NN):
        a += 1
        time.sleep(0.1)
    # end of thread2

thread1 = Thread(target=thread1, args=("Thread1",))
thread2 = Thread(target=thread2, args=("Thread2",))

thread1.start()
thread2.start()

thread2.join()
# end of ThreadTest01.py

正如所预料的,在运行示例时,“无法到达”的代码有时实际上会被到达,并产生输出。

补充一下,当我在线程 1 中插入一个锁获取/释放对时,我发现打印“无法访问”消息的概率大大降低。为了查看该消息,我将休眠时间缩短至 0.01 秒,并将 NN 增加至 1000。

在线程 1 中有一个锁获取/释放对时,我根本没想到会看到该消息,但它确实在那里。在我将锁获取/释放对也插入到线程 2 中后,该消息不再出现。事后看来,线程 2 中的增量语句可能也是非原子的。

解决方案 5:

嗯,运行示例:

警告!切勿在家/工作时这样做!只能在教室里做 ;)

使用信号量、共享变量等来避免紧急状况。

from threading import Thread
import time

a = 0  # global variable


def thread1(threadname):
    global a
    for k in range(100):
        print("{} {}".format(threadname, a))
        time.sleep(0.1)
        if k == 5:
            a += 100


def thread2(threadname):
    global a
    for k in range(10):
        a += 1
        time.sleep(0.2)


thread1 = Thread(target=thread1, args=("Thread-1",))
thread2 = Thread(target=thread2, args=("Thread-2",))

thread1.start()
thread2.start()

thread1.join()
thread2.join()

输出:

Thread-1 0
Thread-1 1
Thread-1 2
Thread-1 2
Thread-1 3
Thread-1 3
Thread-1 104
Thread-1 104
Thread-1 105
Thread-1 105
Thread-1 106
Thread-1 106
Thread-1 107
Thread-1 107
Thread-1 108
Thread-1 108
Thread-1 109
Thread-1 109
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110
Thread-1 110

如果时机正确,该a += 100操作将被跳过:

处理器在 T 处执行a+100并得到 104。但它停止并跳转到下一个线程。此时,在 T+1 处a+1使用 a 的旧值执行a == 4。因此它计算出 5。跳回(在 T+2 处),线程 1,并写入a=104内存。现在回到线程 2,时间为 T+3 并写入a=5内存。瞧!下一个打印指令将打印 5 而不是 104。

非常讨厌的错误需要重现和捕获。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用