Search code examples
pythonmultithreadingpython-multithreadingpython-internals

Why does my Python thread block the main thread unless I add a print or a sleep?


Does anyone know why running this code causes the script to hang in the thread, unless I uncomment the print, the sleep, or the "if" condition, or remove the try/except? My understanding is that this thread shouldn't block the main thread.

import threading
import time  # noqa

class Something:
    def __init__(self):
        self.thread = threading.Thread(target=self.thread_loop, daemon=True)
        self.keep_looping = True
        self.a = 0

    def start_thread(self) -> None:
        self.thread.start()

    def stop_thread(self) -> None:
        self.keep_looping = False
        print('stop')

    def thread_loop(self) -> None:
        print('thread_loop')
        while self.keep_looping:
            try:
                # print('loop')
                # time.sleep(0.1)
                self.a += 1
                # if self.a == 'xxxx':
                #     print(self.a)
                #     time.sleep(0.1)
            except Exception as err:
                print(err)


blinky = Something()
print('start')
blinky.start_thread()
print('stop')
blinky.stop_thread()
print('done')

Solution

  • Does anyone know why running this code causes the script to hang in the thread...?

    I don't see your script hanging. If I run it, unmodified, I see:

    start
    thread_loop
    stop
    stop
    done
    

    It's a little unclear what you're hoping to accomplish here; there's effectively zero time between your call to start_thread() and your call to stop_thread(), but since thread_loop() implements a strictly CPU-bound loop we can show that something is happening by adding an additional print to the end of your code:

    blinky = Something()
    print('start')
    blinky.start_thread()
    print('stop')
    blinky.stop_thread()
    print('done')
    print(blinky.a)
    

    Given the above code, we see:

    start
    thread_loop
    stop
    stop
    done
    159022
    

    Showing us that thread_loop looped 159,022 times (the actual number will vary based on the speed of your CPU, the number of CPUs in your system, the number of other programs running, etc).

    It's possible that on your system the loop in thread_loop is consuming so much of your CPU that nothing else has the time to run...in which case anything that explicitly sleeps (such as time.sleep()) or engages in i/o (such as a print statement) would yield control of the processor long enough for another thread to run.

    Threading in Python is primarily useful when your code is not primarily CPU bound. Because of the global interpreter lock, multiple threads in Python are not able to run in parallel, leading to the situation I described in the previous paragraph.