Concurrency Techniques
Overview of Concurrency in Python
Concurrency is a powerful approach to managing multiple tasks simultaneously, allowing developers to create more efficient and responsive applications. Python offers several techniques for implementing concurrent operations.
Major Concurrency Approaches
1. Threading
graph LR
A[Main Thread] --> B[Thread 1]
A --> C[Thread 2]
A --> D[Thread 3]
Threading Implementation
import threading
import queue
def worker(task_queue):
while not task_queue.empty():
task = task_queue.get()
print(f"Processing task: {task}")
task_queue.task_done()
## Create a task queue
task_queue = queue.Queue()
for i in range(10):
task_queue.put(f"Task-{i}")
## Create multiple threads
threads = []
for _ in range(3):
thread = threading.Thread(target=worker, args=(task_queue,))
thread.start()
threads.append(thread)
## Wait for all tasks to complete
task_queue.join()
2. Multiprocessing
graph LR
A[Main Process] --> B[Process 1]
A --> C[Process 2]
A --> D[Process 3]
Multiprocessing Implementation
from multiprocessing import Process, Queue
def worker(task_queue):
while not task_queue.empty():
task = task_queue.get()
print(f"Processing task in process: {task}")
## Create a multiprocessing queue
task_queue = Queue()
for i in range(10):
task_queue.put(f"Task-{i}")
## Create multiple processes
processes = []
for _ in range(3):
process = Process(target=worker, args=(task_queue,))
process.start()
processes.append(process)
## Wait for all processes to complete
for process in processes:
process.join()
Concurrency Techniques Comparison
Technique |
Best For |
Pros |
Cons |
Threading |
I/O-bound tasks |
Lightweight, shared memory |
Global Interpreter Lock (GIL) |
Multiprocessing |
CPU-bound tasks |
True parallelism |
Higher memory overhead |
Async Programming |
Network operations |
Non-blocking |
Complex error handling |
3. Asynchronous Programming
import asyncio
async def task_executor(name, duration):
print(f"Task {name} started")
await asyncio.sleep(duration)
print(f"Task {name} completed")
async def main():
tasks = [
asyncio.create_task(task_executor("Task1", 2)),
asyncio.create_task(task_executor("Task2", 1)),
asyncio.create_task(task_executor("Task3", 3))
]
await asyncio.gather(*tasks)
## Run the async main function
asyncio.run(main())
Choosing the Right Technique
When selecting a concurrency approach, consider:
- Nature of tasks (I/O-bound vs CPU-bound)
- Performance requirements
- Complexity of implementation
LabEx recommends experimenting with different techniques to find the most suitable approach for your specific use case.