Python 在线程中使用全局变量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19790570/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 14:38:07  来源:igfitidea点击:

Using a global variable with a thread

pythonmultithreadingglobal-variables

提问by Mauro Midolo

How do I share a global variable with thread?

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

My Python code example is:

我的 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()

I don't know how to get the two threads to share one variable.

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

采纳答案by chepner

You just need to declare aas a global in thread2, so that you aren't modifying an athat is local to that function.

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

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

In thread1, you don't need to do anything special, as long as you don't try to modify the value of a(which would create a local variable that shadows the global one; use global aif you need to)>

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

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

回答by val

In a function:

在一个函数中:

a += 1

will be interpreted by the compiler as assign to a => Create local variable a, which is not what you want. It will probably fail with a a not initializederror since the (local) a has indeed not been initialized:

将被编译器解释为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

You might get what you want with the (very frowned upon, and for good reasons) globalkeyword, like so:

您可能会使用(非常不赞成,并且有充分的理由)global关键字获得您想要的内容,如下所示:

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

In general however, you should avoidusing global variables which become extremely quickly out of hand. And this is especially true for multithreaded programs, where you don't have any synchronization mechanism for your thread1to know when ahas been modified. In short: threads are complicated, and you cannot expect to have an intuitive understanding of the order in which events are happening when two (or more) threads work on the same value. The language, compiler, OS, processor... can ALL play a role, and decide to modify the order of operations for speed, practicality or any other reason.

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

The proper way for this kind of thing is to use Python sharing tools (locksand friends), or better, communicate data via a Queueinstead of sharing it, e.g. like this:

这种事情的正确方法是使用 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()

回答by visoft

Well, running example:

好吧,运行示例:

WARNING! NEVER DO THIS AT HOME/WORK!Only in classroom ;)

警告!永远不要在家里/工作中这样做!只在课堂上 ;)

Use semaphores, shared variables, etc. to avoid rush conditions.

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

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()

and the output:

和输出:

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

If the timing were right, the a += 100operation would be skipped:

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

Processor executes at T a+100and gets 104. But it stops, and jumps to next thread Here, At T+1 executes a+1with old value of a, a == 4. So it computes 5. Jump back (at T+2), thread 1, and write a=104in memory. Now back at thread 2, time is T+3 and write a=5in memory. Voila! The next print instruction will print 5 instead of 104.

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

VERY nasty bug to be reproduced and caught.

非常讨厌的错误被复制和捕获。

回答by Jason Pan

A lock should be considered to use, such as threading.Lock. See lock-objectsfor more info.

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

The accepted answer CAN print 10 by thread1, which is not what you want. You can run the following code to understand the bug more easily.

接受的答案 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

Using a lock can forbid changing of awhile reading more than one time:

使用锁可以禁止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()

If thread using the variable for long time, coping it to a local variable first is a good choice.

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

回答by Krista M Hill

Thanks so much Jason Pan for suggesting that method. The thread1 if statement is not atomic, so that while that statement executes, it's possible for thread2 to intrude on thread1, allowing non-reachable code to be reached. I've organized ideas from the prior posts into a complete demonstration program (below) that I ran with Python 2.7.

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

With some thoughtful analysis I'm sure we could gain further insight, but for now I think it's important to demonstrate what happens when non-atomic behavior meets threading.

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

# 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

As predicted, in running the example, the "unreachable" code sometimes is actually reached, producing output.

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

Just to add, when I inserted a lock acquire/release pair into thread1 I found that the probability of having the "unreachable" message print was greatly reduced. To see the message I reduced the sleep time to 0.01 sec and increased NN to 1000.

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

With a lock acquire/release pair in thread1 I didn't expect to see the message at all, but it's there. After I inserted a lock acquire/release pair also into thread2, the message no longer appeared. In hind signt, the increment statement in thread2 probably also is non-atomic.

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