0Pricing
Python Academy · Lesson

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.
  • Pool and ProcessPoolExecutor distribute 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

  1. Threads and the GIL
  2. ThreadPoolExecutor
  3. multiprocessing Basics
  4. Sharing Data Safely
← Back to Python Academy