""uses mutexes to know when threads are done in parent/main threadinstead of time sleeplock stdout to avoid comingled prints""import _thread as thread stdoutmutex thread allocate_lock(exitmutexes [thread allocate_lock(for in range( )def counter(myidcount)for in range(count)stdoutmutex acquire(print('[% =% (myidi)stdoutmutex release(exitmutexes[myidacquire(signal main thread for in range( )thread start_new_thread(counter( )for mutex in exitmutexeswhile not mutex locked()pass print('main thread exiting ' lock' locked method can be used to check its state to make this workthe main thread makes one lock per child and tacks them onto global exitmutexes list (rememberthe threaded function shares global scope with the main threadon exiteach thread acquires its lock on the listand the main thread simply watches for all locks to be acquired this is much more accurate than naively sleeping while child threads run in hopes that all will have exited after the sleep run this on your own to see its output--all spawned threads count up to (they run in arbitrarily interleaved order that can vary per run and platformbut their prints run atomically and do not comingle)before the main thread exits depending on how your threads runthis could be even simplersince threads share global memory anyhowwe can usually achieve the same effect with simple global list of integers instead of locks in example - the module' namespace (scopeis shared by top-level code and the threaded functionas before exitmutexes refers to the same list object in the main thread and all threads it spawns because of thatchanges made in thread are still noticed in the main thread without resorting to extra locks example - pp \system\threads\thread-count-wait py ""uses simple shared global data (not mutexesto know when threads are done in parent/main threadthreads share list but not its itemsassumes list won' move in memory once it has been created initially ""import _thread as thread stdoutmutex thread allocate_lock( parallel system tools