multiprocessing
tqdm
progress bar
Python
parallel processing

Multiprocessing use tqdm to display a progress bar

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Using tqdm with Python multiprocessing works best when the main process updates the progress bar as worker results come back. The progress bar should usually stay out of the worker processes themselves, because multiple processes writing to the same terminal creates noisy and unreliable output.

Use imap or imap_unordered

The cleanest pattern is to wrap the iterator returned by the pool with tqdm. Every time one result is yielded, the main process advances the bar by one.

python
1from multiprocessing import Pool
2from time import sleep
3from tqdm import tqdm
4
5
6def work(x):
7    sleep(0.2)
8    return x * x
9
10
11if __name__ == "__main__":
12    items = list(range(20))
13
14    with Pool(processes=4) as pool:
15        results = list(tqdm(pool.imap(work, items), total=len(items)))
16
17    print(results)

This works because pool.imap produces results lazily. tqdm only needs to know how many total items exist and when the next completed result arrives.

Prefer imap_unordered for Better Responsiveness

If task durations vary, imap_unordered often produces a smoother progress bar because the main process does not wait for earlier slow tasks before showing later completed tasks.

python
1from multiprocessing import Pool
2from time import sleep
3from tqdm import tqdm
4
5
6def work(x):
7    sleep(0.1 + (x % 3) * 0.2)
8    return x
9
10
11if __name__ == "__main__":
12    items = list(range(30))
13
14    with Pool(processes=4) as pool:
15        for result in tqdm(pool.imap_unordered(work, items), total=len(items)):
16            pass

If output order matters, collect results with their original indexes or use imap. If progress responsiveness matters more than order, imap_unordered is usually the better choice.

Why map Is Not Ideal for Progress

pool.map waits for all work to finish before returning the full list. That means the progress bar cannot update incrementally in the same way unless you add a more complex callback-based design.

For that reason, most practical tqdm plus multiprocessing examples use:

  • 'imap'
  • 'imap_unordered'
  • 'apply_async with callbacks'

The iterator-based versions are the simplest to maintain.

Callback Pattern for Fine Control

If you need to store results separately or perform side effects when tasks finish, use apply_async and update the bar in a callback owned by the main process.

python
1from multiprocessing import Pool
2from time import sleep
3from tqdm import tqdm
4
5
6def work(x):
7    sleep(0.2)
8    return x * 10
9
10
11if __name__ == "__main__":
12    items = list(range(10))
13    results = []
14
15    with Pool(processes=4) as pool, tqdm(total=len(items)) as pbar:
16        def on_done(result):
17            results.append(result)
18            pbar.update(1)
19
20        jobs = [pool.apply_async(work, args=(item,), callback=on_done) for item in items]
21
22        for job in jobs:
23            job.wait()
24
25    print(results)

This gives you more control, but it is more code than wrapping imap.

Windows and __main__

When using multiprocessing, especially on Windows, always protect process creation with:

python
if __name__ == "__main__":

Without that guard, the module can recursively spawn new processes and fail before the progress bar becomes relevant.

Keep Result Collection Separate from Progress

A progress bar only tells you how many tasks finished. It should not decide how results are stored, ordered, or retried. Keeping those concerns separate makes the code easier to maintain, especially when you later add error handling or partial-result persistence.

Common Pitfalls

  • Updating the progress bar from inside worker processes instead of the main process.
  • Using pool.map and expecting a live bar without additional control flow.
  • Forgetting the total argument, which makes the progress bar less informative.
  • Assuming ordered output is necessary when imap_unordered would provide a smoother bar.
  • Omitting the if __name__ == "__main__": guard in multiprocessing code.

Summary

  • Keep tqdm in the main process and let workers do only the computation.
  • 'pool.imap and pool.imap_unordered are the simplest ways to get a live progress bar.'
  • Use imap_unordered when task durations vary and output order does not matter.
  • Use callbacks only when you need finer control over result handling.
  • Protect multiprocessing entry points with if __name__ == "__main__":.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.