lxacoder
lxacoder

Reputation: 301

Why this 10 threads always output the same thread name?

I ran this code

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

The output is

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

I know why the NUM is always 10, but why is the thread name always the same? Every thread runs print(t.getName()+":"+"NUM is "+str(NUM)); the t should not be the thread that gets the cpu time's one? I think the name should not be the same.

When I changed to this

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

It works as I expect:

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

Upvotes: 3

Views: 676

Answers (4)

Nin
Nin

Reputation: 407

I advice you to try this:

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

Result:

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() is a function call, which is a reference. By the time the print functions reached the console, t references to the latest thread.

Upvotes: 1

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476503

Your function queries for a t, but no t is defined in the function:

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

The fallback mechanism of Python thus will look for a t in the direct outer scope. And indeed, you assign to a t in the outer scope, so it will take that value.

Now since you write t = ... in a for loop, that t changes rapidly. It is very likely that the for loop has already reached the last value - especially because of the threading mechanism of Python - before the first thread actually fetches that t. As a result, all the threads fetch the t referring to the last constructed thread.

If we however rewrite the function to:

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

I get:

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

on my machine. Of course this does not guarantee that every thread will grab the correct thread, since it is possible that only later in the process, the thread will start working and fetches the t variable.

Upvotes: 4

Tim Peters
Tim Peters

Reputation: 70572

It's because you're referencing the global name t. By the time the sleeps end, the loop is over, and t remains bound to the last thread (the 10th thread) the loop created.

In your alternative, the results aren't actually defined. There you reference the global t while the loop is still running, so it's likely to be bound to the most recent thread created - but doesn't have to be.

Note: if you don't have convenient access to the thread object currently running, you can use

threading.currentThread()

to get it. Then

threading.currentThread().getName()

will return the name of the thread running it.

Upvotes: 7

Prune
Prune

Reputation: 77827

You have the same problem for both the thread name and the count NUM: by the time you get to the first print statement, all 10 threads have been started. You have only one global variable t for the thread, and one global NUM for the count. Thus, all you see is the last value, that for the 10th thread. If you want separate values printed, you need to supply your code with a mechanism to report them as they're launched, or keep a list through which you can iterate.

Upvotes: 2

Related Questions