Python Frequently Asked Questions, Release 3.9.5 Chapter 4. Library and Extension FAQ 52 (continues on next page) (continued from previous page) time.sleep( 10 ) # <---------------------------! But now (on many platforms) the threads don’t run in parallel, but appear to run sequentially, one at a time! The reason is that the OS thread scheduler doesn’t start a new thread until the previous thread is blocked. A simple fix is to add a tiny sleep to the start of the run function: Instead of trying to guess a good delay value for$_{time.sleep()}$ , it’s better to use some kind of semaphore mech- anism. One idea is to use the$_{queue}$module to create a queue object, let each thread append a token to the queue when it finishes, and let the main thread read as many tokens from the queue as there are threads. The easiest way is to use the$_{concurrent.futures}$ module, especially the$_{ThreadPoolExecutor}$class. Or, if you want fine control over the dispatching algorithm, you can write your own logic manually. Use the$_{queue}$ module to create a queue containing a list of jobs. The$_{Queue}$ class maintains a list of objects and has a$_{.put(obj)}$ method that adds items to the queue and a$_{.get()}$ method to return them. The class will take care of the locking necessary to ensure that each job is handed out exactly once. Here’s a trivial example: import threading , queue , time # The worker thread gets jobs off the queue. When the queue is empty, it # assumes there will be no more work and exits. # (Realistically workers will run until terminated.) def worker(): print('Running worker') time.sleep( 0.1 ) while True : try : arg =q.get(block = False ) except queue.Empty: print('Worker ', threading.currentThread(), end ='') print('queue empty') break else : print('Worker ', threading.currentThread(), end ='') print('running with argument', arg) time.sleep( 0.5 ) # Create queue q =queue.Queue() # Start a pool of 5 workers for i in range(5): t =threading.Thread(target=worker, name='worker %i '%(i+1)) def thread_task(name, n): time.sleep( 0.001 ) # <--------------------! for i in range(n): print(name, i) for i in range(10 ): T =threading.Thread(target=thread_task, args =(str(i), i)) T.start() time.sleep( 10 ) 4.3.3 How do I parcel out work among a bunch of worker threads?