threading appears to run threads sequentially

Question:

I am trying to use threads in a Python project I am working on, but threads don’t appear to be behaving as they are supposed to in my code. It seems that all threads run sequentially (i.e. thread2 starts after thread 1 ends, they don’t both start at the same time). I wrote a simple script to test this, and that too runs threads sequentially.

import threading

def something():
    for i in xrange(10):
        print "Hello"

def my_thing():
    for i in xrange(10):
        print "world"   

threading.Thread(target=something).start()
threading.Thread(target=my_thing).start() 

Here’s the output I get from running it:

Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
world
world
world
world
world
world
world
world
world
world

The same behavior is observed with much larger number of iterations of the loops.

I tried searching the web and older SO answers, but I couldn’t find anything that helped.
Can someone please point out what is wrong with this code?

Asked By: MAK

||

Answers:

In the time it takes the second thread to start the first thread loops and prints already.

Here it looks like this, you can see the 2nd thread starting after the first emitted a few hellos.

Hello
Hello
Hello
Hello
Hello
Helloworld

Helloworld

Helloworld

Helloworld

Helloworld

world
world
world
world
world

Btw: Your example is not meaningful at all. The only reason for Threads is IO, and IO is slow. When you add some sleeping to simulate IO it should work as expected:

import threading
from time import sleep

def something():
    for i in xrange(10):
        sleep(0.01)
        print "Hello"

def my_thing():
    for i in xrange(10):
        sleep(0.01)
        print "world"

threading.Thread(target=something).start()
threading.Thread(target=my_thing).start()

a wild mix appears:

worldHello

Helloworld

Helloworld

worldHello

Helloworld

Helloworld

worldHello

Helloworld

worldHello

Helloworld
Answered By: Jochen Ritzel

This really depends on your Operating System’s scheduler, your processor.
Other than that, it is known that CPython’s threads aren’t perfect because of the GIL(PDF), which, in short, means that a lot of the times threads do run sequentially, or something of that sort.

Answered By: abyx

Currently in python, threads get changed after executing some specified amount of bytecode instructions. They don’t run at the same time. You will only have threads executing in parallel when one of them calls some I/O-intensive or not python-affecting module that can release GIL (global interpreter lock).

I’m pretty sure you will get the output mixed up if you bump the number of loops to something like 10000. Remember that simply spawning the second thread also takes “a lot” of time.

Answered By: viraptor

The behaviour may also change depending on if the system is using has a single processor or multiple processors, as explained by this talk by David Beazley.

As viraptor says, the first thread will release the GIL after executing sys.getcheckinterval() bytecodes (100 by default). To crudly summarise what David Beazley says, on a single processor system the second thread will then have a chance to take over. However on a multi-core system the second thread may be running on a different core, and the first thread will try to reacquire the lock and will probably succeed since the OS will not have had time to switch processors. This means that on a multi-core system with a CPU-bound thread the other threads may never get a look in.

The way round this is to add a sleep statement to both of the loops so that they are no longer CPU bound.

Answered By: Dave Kirby

Generally, the secret is to always ensure when you are starting the threads you use the below syntax.

Note threading.Thread(target=threadSet1).start() the function threadSet1 has not brackets at the end.

import threading
from time import sleep

def spawn(num, typex):
    import time
    start = time.time()
    print(num, typex)

def threadSet1():
    for i in range(1000):
        sleep(0.01)
        t = threading.Thread(target=spawn(i, "world"))
    t.daemon = True
    t.start()
def threadSet2():
    for i in range(1000):
        sleep(0.01)
        t = threading.Thread(target=spawn(i, "hello"))
        t.daemon = True
        t.start()
if __name__ == '__main__':
    threading.Thread(target=threadSet1).start()
    threading.Thread(target=threadSet2).start()

Do not fall into the trap of using the below syntax as it will execute them serially
Note the functions have brackets at the end.
threading.Thread(target=threadSet1()).start()
threading.Thread(target=threadSet2()).start()

Answered By: sospeter mule
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.