Savoga

Threads


Bounds

To know if multithreading can speed up a task, one should know the types of bounds that are saturated:

  • CPU-bound (e.g. calculation) -> Python: multithreading doesn’t work (because of the GIL), multiprocessing is the workaround / C++: multithreading works

  • I/O-bound (e.g. accessing files using an external system) -> concurrency (async or multithreading) helps

  • memory-bound (RAM, e.g. copying data)

  • other types of bounds (less common)

Python multiprocessing

In Python, the 2 main ways to do multiprocessing is to use:

multiprocessing: more control over processes

concurrent.futures: for simple parallelism (uses multiprocessing under the hood)

When using multiprocessing with the spawn start method (default on Windows and macOS), processes start a fresh Python instance that imports the main (highest-level) module. Therefore any top-level code (like creating a ProcessPoolExecutor or calling .map()) would be re-executed in each process , potentially causing recursive process creation. Using if name == “main”: prevents those side effects from running in child processes.


# No guard => spawn problem!

# --------- using multiprocessing

from multiprocessing import Process

def work(x):
    print(x * x)

p = Process(target=work, args=(5,))
p.start()
p.join()

# --------- using concurrent.futures

from concurrent.futures import ProcessPoolExecutor

def work(x):
    return x * x

executor = ProcessPoolExecutor()
results = executor.map(work, range(4))
print(list(results))

In the above script (no guard), on spawn-based platforms (Windows/Mac), when workers start (typically at .map step) they re-run top-level code, so they will create new executors again. This can cause recursive spawning (effectively “infinite” processes).

Start

Use std::thread t1(do_something);

.detach(): let run the thread in the background independently. Ideally stated right after starting the thread.

Finish

.join(): used to wait for a thread to finish. Note: we say “join” because the thread finishes in joining others e.g. it finishes in joining the main thread (example below).


int main() { 
	std::thread t(do_something);
	t.join();
}