使用带线程的全局变量

Mau*_*olo 69 python multithreading global-variables

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

我的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()
Run Code Online (Sandbox Code Playgroud)

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

che*_*ner 80

您只需要声明a为全局输入thread2,这样您就不会修改a该函数的本地属性.

def thread2(threadname):
    global a
    while True:
        a += 1
        time.sleep(1)
Run Code Online (Sandbox Code Playgroud)

thread1,你不需要做任何特殊的事情,只要你不试图修改a(它会创建一个影响全局变量的局部变量; global a如果需要,可以使用)>

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

  • 是否需要在文件头添加“lock = threading.Lock()”,并在“thread2”中添加“with lock:”来保护“a+=1”? (2认同)
  • 几乎可以确定。我在非常狭隘的意义上回答了这个问题:如何修改全局变量,而不考虑如何*安全地*修改它。 (2认同)

val*_*val 45

在一个功能:

a += 1
Run Code Online (Sandbox Code Playgroud)

将被编译器解释为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
Run Code Online (Sandbox Code Playgroud)

你可能会得到你想要的东西(非常皱眉,有充分理由)global关键字,如下:

>>> def f():
...     global a
...     a += 1
... 
>>> a
1
>>> f()
>>> a
2
Run Code Online (Sandbox Code Playgroud)

但是,一般情况下,您应该避免使用极其快速失控的全局变量.对于多线程程序尤其如此,在这些程序中,您没有任何同步机制可以让您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()
Run Code Online (Sandbox Code Playgroud)

  • 这已经很旧了,但我还是回答。队列本身不是同步的,只不过是变量“a”。这是创建同步的队列的默认阻塞行为。语句 `a = q.get()` 将阻塞(等待)直到值 a 可用。变量“q”是本地的:如果你给它分配一个不同的值,它只会在本地发生。但代码中分配给它的队列是在主线程中定义的。 (2认同)
  • 并不总是需要使用队列在线程之间共享信息。切普纳答案中的例子非常好。此外,队列并不总是正确的工具。例如,如果您想阻塞直到该值可用,则队列很有用。如果两个线程竞争共享资源,这是没有用的。最后,全局变量在线程中并不是最糟糕的。事实上它们可以更自然。例如,您的线程可能只是一个需要自己的进程的代码块(例如循环)。因此,当您将循环放入函数中时,会人为地创建局部作用域。 (2认同)

Jas*_*Pan 5

应考虑使用锁,例如threading.Lock. 有关更多信息,请参阅锁定对象

接受的答案 CAN print 10 by thread1,这不是您想要的。您可以运行以下代码以更轻松地了解该错误。

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

def thread2(threadname):
    global a
    while True:
        a += 1
Run Code Online (Sandbox Code Playgroud)

使用锁可以禁止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()
Run Code Online (Sandbox Code Playgroud)

如果线程长时间使用该变量,首先将其处理为局部变量是一个不错的选择。


Kri*_*ill 5

非常感谢 Jason Pan 提出这个方法。thread1 if 语句不是原子的,因此当该语句执行时,thread2 可能会侵入 thread1,从而允许访问不可访问的代码。我已将之前帖子中的想法组织成一个完整的演示程序(如下),并使用 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
Run Code Online (Sandbox Code Playgroud)

正如预测的那样,在运行该示例时,有时实际上会到达“无法访问”的代码,并产生输出。

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

对于线程 1 中的锁获取/释放对,我根本没想到会看到该消息,但它就在那里。当我将锁获取/释放对也插入到 thread2 中后,该消息不再出现。在后签名中,thread2 中的增量语句可能也是非原子的。