multiprocessing Basics
Use multiple CPU cores.
multiprocessing Basics is a free Python Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Python Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why multiprocessing?
The GIL stops threads from running Python bytecode in parallel. The multiprocessing module sidesteps this by launching separate processes, each with its own interpreter and GIL, so CPU-bound work truly runs on multiple cores.
import multiprocessing as mp
print('CPU cores available:', mp.cpu_count())Starting a Process
Process(target=func) mirrors the threading API. Call start() to launch and join() to wait.
import multiprocessing as mp
def worker():
print('hello from a child process')
if __name__ == '__main__':
p = mp.Process(target=worker)
p.start()
p.join()
print('parent done')The __main__ Guard
On some platforms child processes import your script. Without an if __name__ == '__main__': guard you can spawn processes recursively. Always wrap the launch code in it.
import multiprocessing as mp
def task(n):
print('processing', n)
if __name__ == '__main__':
for i in range(3):
p = mp.Process(target=task, args=(i,))
p.start()
p.join()Passing Arguments
Use args for positional and kwargs for keyword arguments, just like threads. Arguments are pickled and sent to the child.
import multiprocessing as mp
def show(label, times):
for _ in range(times):
print(label)
if __name__ == '__main__':
p = mp.Process(target=show, args=('hi',), kwargs={'times': 2})
p.start()
p.join()A Pool of Workers
multiprocessing.Pool manages a group of processes. pool.map splits an iterable across them and gathers results in order.
import multiprocessing as mp
def square(n):
return n * n
if __name__ == '__main__':
with mp.Pool(processes=2) as pool:
print(pool.map(square, range(6)))ProcessPoolExecutor
The concurrent.futures API also offers ProcessPoolExecutor, with the same submit/map interface you saw for threads.
from concurrent.futures import ProcessPoolExecutor
def cube(n):
return n ** 3
if __name__ == '__main__':
with ProcessPoolExecutor() as ex:
print(list(ex.map(cube, range(5))))Separate Memory Spaces
Each process has its own memory. Modifying a global in a child does not change the parent's copy. Data must be returned or shared explicitly.
import multiprocessing as mp
counter = 0
def bump():
global counter
counter += 100
if __name__ == '__main__':
p = mp.Process(target=bump)
p.start()
p.join()
print('parent counter still:', counter)Getting Results Back
Because memory is not shared, return results through Pool.map, executors, or queues rather than mutating globals.
import multiprocessing as mp
def double(n):
return n * 2
if __name__ == '__main__':
with mp.Pool(2) as pool:
results = pool.map(double, [1, 2, 3, 4])
print('results:', results)apply_async for Single Calls
apply_async schedules one call and returns a result handle. Call .get() to retrieve the value.
import multiprocessing as mp
def add(a, b):
return a + b
if __name__ == '__main__':
with mp.Pool(2) as pool:
r = pool.apply_async(add, (3, 4))
print(r.get())Pickling Requirement
Targets and arguments must be picklable so they can cross the process boundary. Top-level functions work; lambdas and local functions usually do not.
import multiprocessing as mp
def top_level(n):
return n + 1
if __name__ == '__main__':
with mp.Pool(2) as pool:
print(pool.map(top_level, [10, 20]))When to Reach for Processes
Use processes for CPU-bound work: math, image processing, parsing. The overhead of spawning and pickling means they suit fewer, heavier tasks rather than many tiny ones.
import multiprocessing as mp
def heavy(n):
return sum(i * i for i in range(n))
if __name__ == '__main__':
with mp.Pool() as pool:
print(pool.map(heavy, [1000, 2000, 3000]))Quick Check
Test your understanding of multiprocessing.
Recap
You learned multiprocessing basics:
- Processes bypass the GIL for real CPU parallelism.
- Use the
if __name__ == '__main__'guard. PoolandProcessPoolExecutordistribute work and gather results.- Memory is not shared; arguments must be picklable.
Next: sharing data safely with queues and locks.
Frequently asked questions
Is the “multiprocessing Basics” lesson free?
Yes — the full text of “multiprocessing Basics” is free to read here on the web, and the Python Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Python Academy course, upgrade to CoddyKit PRO.
What will I learn in “multiprocessing Basics”?
Use multiple CPU cores. You practise Python Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Python Academy?
No prior experience is required. Python Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “multiprocessing Basics” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Python Academy lesson?
Yes. Every Python Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Threads and the GIL
- ThreadPoolExecutor
- multiprocessing Basics
- Sharing Data Safely