Python
multiprocessing
function return
parallel computing
Process class

How can I get the return value of a function passed to multiprocessing.Process?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A function passed to multiprocessing.Process does not return a value directly to the parent process. The child runs in a separate process with separate memory, so you have to send results back explicitly through inter-process communication.

Why A Normal Return Statement Does Not Help

A Process target looks like a normal Python function, but it is not being called in the same address space as the parent.

python
1from multiprocessing import Process
2
3
4def worker(x):
5    return x * 2
6
7
8if __name__ == "__main__":
9    p = Process(target=worker, args=(10,))
10    p.start()
11    p.join()

worker returns 20, but the parent never receives it. The process simply exits.

So the real question is not "how do I access the return value" but "which IPC primitive should carry the result back."

Use A Queue For General Results

A queue is the most common answer.

python
1from multiprocessing import Process, Queue
2
3
4def worker(x, out_q):
5    out_q.put(x * 2)
6
7
8if __name__ == "__main__":
9    q = Queue()
10    p = Process(target=worker, args=(10, q))
11    p.start()
12    result = q.get()
13    p.join()
14    print(result)

This works well for one result, many results, or even structured payloads.

A queue is usually the right first choice because it scales to multiple workers and keeps the parent-child contract explicit.

Use A Pipe For One-To-One Communication

If there is exactly one child and one parent exchanging a small amount of data, Pipe is simpler.

python
1from multiprocessing import Process, Pipe
2
3
4def worker(conn, x):
5    conn.send(x ** 2)
6    conn.close()
7
8
9if __name__ == "__main__":
10    parent_conn, child_conn = Pipe()
11    p = Process(target=worker, args=(child_conn, 12))
12    p.start()
13    print(parent_conn.recv())
14    p.join()

This is lightweight, but less convenient than a queue if the design grows beyond one child and one message stream.

Use A Pool If You Really Want Return Values

If your workload is really "run a function on many inputs and collect the results," multiprocessing.Pool is a better abstraction than manual Process objects.

python
1from multiprocessing import Pool
2
3
4def worker(x):
5    return x * 3
6
7
8if __name__ == "__main__":
9    with Pool(processes=4) as pool:
10        results = pool.map(worker, [1, 2, 3, 4])
11    print(results)

This is closer to ordinary function return semantics and is often what people actually want.

Handling Errors Properly

A child process can fail before sending a result. If the parent blindly waits on q.get() or recv(), it can block forever.

A safer queue pattern is to send structured output:

python
1from multiprocessing import Process, Queue
2
3
4def worker(x, out_q):
5    try:
6        out_q.put((True, 100 / x))
7    except Exception as exc:
8        out_q.put((False, str(exc)))
9
10
11if __name__ == "__main__":
12    q = Queue()
13    p = Process(target=worker, args=(0, q))
14    p.start()
15    ok, payload = q.get()
16    p.join()
17    print(ok, payload)

This makes failures visible to the parent instead of disappearing inside the child.

Do Not Forget The Main Guard

On platforms that use spawn-based startup, especially Windows, always protect process creation with:

python
if __name__ == "__main__":

Without that guard, the module can be re-imported during child startup and create runaway process spawning.

A Modern Alternative

If you want futures and normal-looking result retrieval, concurrent.futures.ProcessPoolExecutor is often easier to integrate:

python
1from concurrent.futures import ProcessPoolExecutor
2
3
4def worker(x):
5    return x + 10
6
7
8if __name__ == "__main__":
9    with ProcessPoolExecutor(max_workers=2) as executor:
10        future = executor.submit(worker, 5)
11        print(future.result())

This is not the same API as Process, but for many applications it is the better tool.

Common Pitfalls

The most common mistake is expecting a Process target's return value to come back automatically. It will not.

Another mistake is using global variables to share results. Separate processes do not share ordinary Python objects the way threads do.

Developers also block forever waiting for a queue item that the child never sent because the child crashed first.

Finally, some code uses raw Process objects when a pool or executor is the real abstraction needed. That adds boilerplate for no benefit.

Summary

  • 'multiprocessing.Process does not give you direct return values.'
  • Use Queue or Pipe to send results back explicitly.
  • Use Pool or ProcessPoolExecutor when you want function-like result handling.
  • Send structured success and error payloads for robustness.
  • Always use the if __name__ == "__main__": guard when starting child processes.

Course illustration
Course illustration

All Rights Reserved.