๐Ÿš€ Python Tutorial โ€” concurrent.futures (ThreadPool & ProcessPool)

Introduction ๐ŸŒŸ

The concurrent.futures module provides a high-level interface for running tasks concurrently using:

  • ๐Ÿงต ThreadPoolExecutor โ†’ Best for I/O-bound tasks
  • ๐Ÿง  ProcessPoolExecutor โ†’ Best for CPU-bound tasks

Note

๐Ÿ’ก Simple API compared to raw threading/multiprocessing
๐Ÿ’ก Future objects represent results of running tasks
๐Ÿ’ก Provides map(), submit(), as_completed()

1. Importing concurrent.futures ๐Ÿงฑ

import_cf.py

import concurrent.futures

2. ThreadPoolExecutor โ€” I/O Bound Tasks โšก

thread_pool.py

from concurrent.futures import ThreadPoolExecutor
import time

def fetch_data(n):
    print(f"Fetching {n}...")
    time.sleep(1)
    return f"Data {n}"

with ThreadPoolExecutor(max_workers=3) as executor:
    results = executor.map(fetch_data, [1,2,3,4,5])

print(list(results))

โœ” Runs tasks concurrently
โœ” Automatically manages threads

3. ProcessPoolExecutor โ€” CPU Bound Tasks ๐Ÿงฎ

process_pool.py

from concurrent.futures import ProcessPoolExecutor
import math

def compute(n):
    return sum(math.sqrt(i) for i in range(n))

with ProcessPoolExecutor() as executor:
    results = executor.map(compute, [10000, 20000, 30000])
    print(list(results))

โœ” True parallelism using multiple CPU cores

4. submit() โ€” Running Single Tasks ๐ŸŽฏ

submit_example.py

from concurrent.futures import ThreadPoolExecutor
import time

def task(n):
    time.sleep(1)
    return n * 2

with ThreadPoolExecutor() as executor:
    future = executor.submit(task, 5)
    print(future.result())

โœ” submit() returns a Future object
โœ” result() blocks until task completes

5. as_completed() โ€” Process Results as They Finish ๐Ÿ

as_completed_example.py

from concurrent.futures import ThreadPoolExecutor, as_completed
import time

def job(n):
    time.sleep(n)
    return f"Finished {n}"

with ThreadPoolExecutor() as ex:
    futures = [ex.submit(job, i) for i in [3,1,2]]

    for f in as_completed(futures):
        print(f.result())

โœ” Outputs results in order of completion, not submission

6. Handling Exceptions in Futures โš ๏ธ

future_exception.py

from concurrent.futures import ThreadPoolExecutor

def bad():
    raise ValueError("Oops!")

with ThreadPoolExecutor() as ex:
    f = ex.submit(bad)
    try:
        f.result()
    except Exception as e:
        print("Caught:", e)

โœ” Exceptions propagate through result()

7. Cancelling Futures ๐Ÿ›‘

cancel_future.py

from concurrent.futures import ThreadPoolExecutor
import time

def long_task():
    time.sleep(3)

with ThreadPoolExecutor() as ex:
    f = ex.submit(long_task)
    canceled = f.cancel()  # only works before running
    print("Canceled?", canceled)

Note

โ— Once a Future has started executing, it cannot be cancelled.

8. Timeout Handling โฑ๏ธ

timeout.py

from concurrent.futures import ThreadPoolExecutor
import time

def wait():
    time.sleep(5)

with ThreadPoolExecutor() as ex:
    f = ex.submit(wait)
    try:
        print(f.result(timeout=2))
    except Exception as e:
        print("Timeout:", e)

9. Difference Between map() and submit() โšก

Featuremap()submit()
ReturnsResults in orderFuture objects
Start immediately?NoYes
Exception accessWhen retrieving resultsDirect via future
Handles multiple argsSingle iterableAny args

10. Real-World Example โ€” Downloading Web Pages ๐ŸŒ

web_scraping.py

from concurrent.futures import ThreadPoolExecutor
import requests

def download(url):
    print("Downloading", url)
    return requests.get(url).status_code

urls = ["https://google.com", "https://example.com"]

with ThreadPoolExecutor(max_workers=2) as ex:
    for status in ex.map(download, urls):
        print("Status:", status)

11. Real-World Example โ€” Parallel Image Processing ๐Ÿ–ผ๏ธ

image_processing.py

from concurrent.futures import ProcessPoolExecutor
from PIL import Image, ImageFilter

def blur_image(path):
    img = Image.open(path)
    img = img.filter(ImageFilter.BLUR)
    img.save("blur_" + path)
    return path

images = ["img1.jpg", "img2.jpg", "img3.jpg"]

with ProcessPoolExecutor() as ex:
    for result in ex.map(blur_image, images):
        print("Processed:", result)

12. Executor Shutdown ๐Ÿ’ผ

shutdown_example.py

executor = ThreadPoolExecutor()
# ...
executor.shutdown(wait=True)  # wait=False = don't wait for tasks

13. concurrent.futures vs Threading vs Multiprocessing โš”๏ธ

FeatureThreadPoolExecutorProcessPoolExecutor
Best forI/O-bound workCPU-bound work
Parallel?No (GIL)Yes
Lightweight?YesNo
Shared memory?YesNo

Best Practices ๐Ÿ’ก

  • โœ” Use ThreadPoolExecutor for I/O tasks
  • โœ” Use ProcessPoolExecutor for CPU-heavy tasks
  • โœ” Use map() for simple iterable tasks
  • โœ” Use submit() for more control
  • โœ” Always handle exceptions from futures
  • โœ” Avoid creating too many workers

Conclusion ๐ŸŽ‰

>>โ€œconcurrent.futures makes concurrency simple โ€” giving you high-level thread and process pools with minimal effort.โ€ โœจ

You now fully understand Concurrent Futures in Python! Want the next topic? Try AsyncIO + Executors, Job Scheduling, or Parallel Algorithms. Just tell me! ๐Ÿ˜Š