Threads and join
Most programs spend their time waiting: for a disk, a network, a database. Concurrency lets those waits overlap, so three downloads that each wait a second can finish in about one second instead of three.
Computing is different. In the standard build of Python, a global interpreter lock, the GIL, lets only one thread run Python code at a time. A thread that sleeps or waits for I/O releases it, so threads speed up waiting, not arithmetic.
import threading
import time
results = {}
def download(name):
time.sleep(0.01) # waiting for I/O
results[name] = len(name)
threads = [
threading.Thread(target=download,
args=(name,))
for name in ["a.txt", "notes.md"]
]
for t in threads:
t.start()
for t in threads:
t.join()
print(sorted(results.items()))start() launches a thread and returns at once. join() waits until it finishes. Start them all, then join them all: joining right after each start runs them one at a time.
A thread cannot hand back a return value, so its function stores the result somewhere you read after join().
Your task
Write run_all(jobs), where jobs is a list of functions that take no arguments. Run every job in its own thread, all at the same time, wait for all of them, and return their results as a list in the order of jobs.
The explanation opens here once every check passes.