简体   繁体   English

在线程中使用全局变量

[英]Using a global variable with a thread

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. 我不知道如何让两个线程共享一个变量。

You just need to declare a as a global in thread2 , so that you aren't modifying an a that is local to that function. 你只需要声明a作为一个全球性的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 a if 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

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. 将由编译器解释为assign to a => Create local variable a ,这不是您想要的。 It will probably fail with a a not initialized error since the (local) a has indeed not been initialized: 由于(local)a确实尚未初始化,它可能会因a not initialized错误而失败:

>>> 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) global keyword, like so: 您可以使用global关键字(由于种种原因而非常皱眉)来获得所需的内容,如下所示:

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

In general however, you should avoid using 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 thread1 to know when a has been modified. 这是多线程程序,在这里你不必为你的任何同步机制尤其如此thread1知道,当a已经被修改。 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. 语言,编译器,OS,处理器...都可以发挥作用,并出于速度,实用性或任何其他原因决定更改操作顺序。

The proper way for this kind of thing is to use Python sharing tools ( locks and friends), or better, communicate data via a Queue instead of sharing it, eg like this: 这种事情的正确方法是使用Python共享工具( 和朋友),或者更好的方法是通过Queue而不是共享数据来传递数据,例如:

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

A lock should be considered to use, such as threading.Lock . 应该考虑使用锁,例如threading.Lock See lock-objects for more info. 有关更多信息,请参见锁定对象

The accepted answer CAN print 10 by thread1, which is not what you want. 可接受的答案可以通过thread1打印10,这不是您想要的。 You can run the following code to understand the bug more easily. 您可以运行以下代码来更轻松地了解该错误。

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 a while 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. 如果线程长时间使用该变量,那么将其首先处理为局部变量是一个不错的选择。

Thanks so much Jason Pan for suggesting that method. 非常感谢Jason Pan提出了该方法。 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. 线程1的if语句不是原子的,因此在执行该语句时,线程2可能会侵入线程1,从而允许到达不可访问的代码。 I've organized ideas from the prior posts into a complete demonstration program (below) that I ran with Python 2.7. 我已经将以前的文章中的想法整理成一个完整的演示程序(如下所示),该程序是我在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. 只需添加一下,当我在线程1中插入一个锁获取/释放对时,我发现打印“不可达”消息的可能性大大降低了。 To see the message I reduced the sleep time to 0.01 sec and increased NN to 1000. 为了看到该消息,我将睡眠时间减少到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. 在thread1中有一个锁获取/释放对,我完全没希望看到该消息,但是它在那里。 After I inserted a lock acquire/release pair also into thread2, the message no longer appeared. 将锁获取/释放对也插入线程2后,该消息不再出现。 In hind signt, the increment statement in thread2 probably also is non-atomic. 在后符号中,thread2中的增量语句可能也是非原子的。

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 += 100 operation would be skipped: 如果时机正确,则将跳过a += 100操作:

Processor executes at T a+100 and gets 104. But it stops, and jumps to next thread Here, At T+1 executes a+1 with old value of a, a == 4 . 处理器在T a+100处执行并得到104。但是它停止并跳转到下一个线程。在T + 1,处理器以旧值a执行a+1a == 4 So it computes 5. Jump back (at T+2), thread 1, and write a=104 in memory. 因此它计算出5。跳回(在T + 2),线程1,并在内存中写入a=104 Now back at thread 2, time is T+3 and write a=5 in memory. 现在回到线程2,时间为T + 3,并在内存中写入a=5 Voila! 瞧! The next print instruction will print 5 instead of 104. 下一条打印指令将打印5而不是104。

VERY nasty bug to be reproduced and caught. 要复制和捕获的非常讨厌的错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM