0

Can someone explain how the Python threading works? Does thread.start() not run the target function to completion before switching back to another context like main below?

import time
import threading

def threadfunc():
    # time.sleep(1)
    print('thread print 1', flush=True)
    print('thread print 2', flush=True)
    #time.sleep(1)

print('before thread', flush=True)

thread1 = threading.Thread(target=threadfunc)
thread1.start()

print('after thread', flush=True)

Output:

before thread
thread print 1
after thread
thread print 2 #shouldn't this be after "print 1"?
TurtleTread
  • 1,297
  • 2
  • 12
  • 23
  • 2
    If `thread.start()` would wait for the thread to complete, then what would be the point of using a thread? – Aran-Fey Jul 27 '19 at 15:57
  • on the countrary. that should not be printed after "print1". you are running that function in an independant thread. so the code will continue the execution. that's why we use thread so thta the code execution will not wait until the function is done, otherwise what's the point of threading in the first case – basilisk Jul 27 '19 at 16:01
  • Both of you fail to explain how exactly the Python code determines the execution. Threads are still blocked by the GIL, so normally you would expect python code to finish before switching outta context. Threads would have to be switched to a non-running state like using a sleep function or doing some IO. Is it because print() is considered an output function that then makes the thread switch context? – TurtleTread Jul 27 '19 at 16:23
  • Re, "...is it because print() is...an output function?" Probably, Yes. I don't know the internals of C Python, but I would assume that any call to the operating system could be a yield point, and I would assume that any system call that potentially does blocking I/O certainly _must_ be a yield point. If that wasn't true, and if you can't use threads for multiprocessing (which you can't because of the GIL), then there would be practically no reasons left for using threads. – Solomon Slow Jul 27 '19 at 16:46
  • 1
    A C Python thread doesn't have to keep the GIL continuously locked the whole time it is running. It only needs to lock it when it is accessing data structures (e.g., any Python object) that potentially are shared with other threads. It could, in principle, unlock, yield, and then lock again in between every two primitive statements. I don't know if that's what it actually does, but I am certain (for the reason I gave above) that it must unlock the GIL before every potentially blocking system call. – Solomon Slow Jul 27 '19 at 16:58

1 Answers1

2

as the comments explained, that's not how threads or the GIL work in Python

lots of the C code underlying CPython will release the GIL, IO operations (as Solomon noted) will do this, while some libraries (e.g. NumPy) explicitly release it just to help multithreaded code go faster

the interpreter will also try and switch threads every few milliseconds

see https://stackoverflow.com/a/49573860/1358308 for some more details

Sam Mason
  • 15,216
  • 1
  • 41
  • 60