๐ 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()
๐ก Future objects represent results of running tasks
๐ก Provides map(), submit(), as_completed()
1. Importing concurrent.futures ๐งฑ
import_cf.py
import concurrent.futures2. 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() โก
| Feature | map() | submit() |
|---|---|---|
| Returns | Results in order | Future objects |
| Start immediately? | No | Yes |
| Exception access | When retrieving results | Direct via future |
| Handles multiple args | Single iterable | Any 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 tasks13. concurrent.futures vs Threading vs Multiprocessing โ๏ธ
| Feature | ThreadPoolExecutor | ProcessPoolExecutor |
|---|---|---|
| Best for | I/O-bound work | CPU-bound work |
| Parallel? | No (GIL) | Yes |
| Lightweight? | Yes | No |
| Shared memory? | Yes | No |
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! ๐