2017-06-20 56 views
3

我跑这个代码为什么这10个线程总是输出相同的线程名称?

NUM = 0 
def count(): 
    global NUM 
    NUM += 1 
    time.sleep(1) 
    print(t.getName()+":"+"NUM is "+str(NUM)) 

for i in range(10): 
    t = threading.Thread(target=count) 
    t.start() 

输出是

Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 
Thread-10:NUM is 10 

我知道为什么NUM始终是10,但为什么是线程的名字总是相同的?每个线程运行print(t.getName()+":"+"NUM is "+str(NUM)); t不应该是获得CPU时间的线程?我认为这个名字不应该是一样的。

当我改变了这个

NUM = 0 
def count(): 
    global NUM 
    NUM += 1 
    name = t.getName() 
    time.sleep(1) 
    print(name+":"+"NUM is "+str(NUM)) 

for i in range(10): 
    t = threading.Thread(target=count) 
    t.start() 

它的工作原理如我所料:

Thread-1:NUM is 10 
Thread-3:NUM is 10 
Thread-2:NUM is 10 
Thread-4:NUM is 10 
Thread-5:NUM is 10 
Thread-7:NUM is 10 
Thread-10:NUM is 10 
Thread-9:NUM is 10 
Thread-6:NUM is 10 
Thread-8:NUM is 10 
+2

“t”在哪里设置? –

+0

't'是一个*外*变量。例如重复设置直到达到10。 –

回答

6

这是因为你参考全球名称t。睡眠结束时,循环结束,并且t保持绑定到创建的循环的最后一个线程(第10个线程)。

在您的替代方案中,结果并未实际定义。有参考全球t循环仍在运行,所以它很可能会被绑定到最近创建的线程 - 但不是是。

注意:如果没有便捷的线程对象正在运行,你可以使用

threading.currentThread() 

得到它。然后

threading.currentThread().getName() 

将返回运行它的线程的名称。

+0

我试过你的代码,它实际上工作。但为什么当使用'threading.currentThread()'工作时,文档说>返回当前的Thread对象,对应于调用者的控制线程。 't'已经被设置为for循环中的第10个线程,'currentThread()'不应该是相同的第10个线程? @Tim Peters – lxacoder

+1

“调用者”是调用'currentThread()'的线程,所以_always_是你想要的线程;-)这与线程的创建无关(特别是,与当时全球的't'碰巧无关)。 –

+1

@lxacoder可以这么想:假设在主线程可以改变它的值之前,对'count'的每次调用运行的时间至少等于对't' *的引用。但为什么呢?唯一可以保证的是每个线程都是*创建的*您无法对每个线程在被再次挂起之前执行多少时间做出任何假设。 – chepner

4

你的t功能查询,但没有t在功能定义:

def count(): 
    global NUM 
    NUM += 1 
    name = t.getName() # use outer t 
    time.sleep(1) 
    print(name+":"+"NUM is "+str(NUM))

回退机制的Python因此将在外部直接范围内寻找t。实际上,您在外部范围内分配了t,因此它将采用该值。

现在,因为你写的for循环t = ...,即t迅速变化。在第一个线程实际获取t之前,很有可能for循环已经达到最后一个值 - 特别是因为Python的线程机制。因此,所有线程都会参考上一个构造的线程获取t

如果我们不过重写功能

NUM = 0 
def count(): 
    name = t.getName() # t fetched immediately 
    global NUM 
    NUM += 1 
    time.sleep(1) 
    print(name+":"+"NUM is "+str(NUM)) 

我得到:

Thread-11:NUM is 10 
Thread-12:NUM is 10 
Thread-13:NUM is 10 
Thread-14:NUM is 10 
Thread-15:NUM is 10 
Thread-17:NUM is 10 
Thread-16:NUM is 10 
Thread-19:NUM is 10 
Thread-18:NUM is 10 
Thread-10:NUM is 10 

我的机器上。当然这是不是保证每个线程都会抓取正确的线程,因为有可能只有在进程后面,线程才会开始工作并取回t变量。

+1

我想我知道这就是为什么在你的帮助之后。谢谢。 – lxacoder

+0

这肯定会减少当前线程在主线程改变之前失去访问't'的可能性,但它并不能消除它。 – chepner

+0

@chepner:是的我知道,我只是想证明在这里我们抓住了不同的't'值。但我在答案的底部添加了一个注释。 –

2

对于线程名称和计数NUM,您都有同样的问题:到达第一个print语句时,所有10个线程都已启动。你只有一个全局变量t为线程,而一个为全局为NUM为计数。因此,你看到的只是第10个线程的最后一个值。如果你想单独打印数据,你需要为你的代码提供一个机制来在启动时报告它们,或者保存一个你可以迭代的列表。

1

我建议你试试这个:

NUM = 0 
def count(): 
    global NUM 
    NUM += 1 
    num = NUM 
    name = t.getName() 
    time.sleep(1) 
    print("t.getName: " + t.getName() + ", name: " + name + ":" + ", NUM: " + str(NUM) + ", num: " + str(num)) 

for i in range(10): 
    t = threading.Thread(target=count) 
    t.start() 

结果:

t.getName: Thread-10, name: Thread-10:, NUM: 10, num: 10 
t.getName: Thread-10, name: Thread-6:, NUM: 10, num: 6 
t.getName: Thread-10, name: Thread-3:, NUM: 10, num: 3 
t.getName: Thread-10, name: Thread-5:, NUM: 10, num: 5 
t.getName: Thread-10, name: Thread-4:, NUM: 10, num: 4 
t.getName: Thread-10, name: Thread-9:, NUM: 10, num: 9 
t.getName: Thread-10, name: Thread-7:, NUM: 10, num: 7 
t.getName: Thread-10, name: Thread-2:, NUM: 10, num: 2 
t.getName: Thread-10, name: Thread-1:, NUM: 10, num: 1 
t.getName: Thread-10, name: Thread-8:, NUM: 10, num: 8 

t.getName()是一个函数调用,这是一个参考。当打印功能到达控制台时,t引用最新的线程。

+0

与任何全局变量一样,'t'受制于竞争条件,并且当任何特定线程的函数实际运行时,您无法确定哪个线程会引用't'。 – chepner