~/victorpierre.dev ❯ cat learning/programming/python/concurrency.md
Concurrency
Concurrency runs multiple tasks in overlapping periods rather than sequentially. The payoff is mostly in I/O-bound work: file reads, network calls, database queries. Python gets there through threads, multiprocessing, or asynchronous programming.
Concurrency in Python vs. Go
Unlike Go, which has built-in support for concurrency through goroutines and channels, Python does not have a direct equivalent. However, Python provides several modules to achieve concurrency:
threadingModule: Used for concurrent execution of code by creating threads.multiprocessingModule: Used for concurrent execution of code by creating separate processes.asyncioModule: Used for asynchronous programming, which is especially useful for I/O-bound tasks.
Using threading Module
The threading module runs multiple threads (smaller units of a process) concurrently.
import threading
import time
def print_numbers():
for i in range(1, 6):
time.sleep(1)
print(f"Number: {i}")
def print_letters():
for letter in 'abcde':
time.sleep(1.5)
print(f"Letter: {letter}")
# Create threads
thread1 = threading.Thread(target=print_numbers)
thread2 = threading.Thread(target=print_letters)
# Start threads
thread1.start()
thread2.start()
# Wait for both threads to complete
thread1.join()
thread2.join()
print("Done!")Using multiprocessing Module
The multiprocessing module runs multiple processes concurrently, the better fit for CPU-bound tasks.
import multiprocessing
import time
def print_numbers():
for i in range(1, 6):
time.sleep(1)
print(f"Number: {i}")
def print_letters():
for letter in 'abcde':
time.sleep(1.5)
print(f"Letter: {letter}")
# Create processes
process1 = multiprocessing.Process(target=print_numbers)
process2 = multiprocessing.Process(target=print_letters)
# Start processes
process1.start()
process2.start()
# Wait for both processes to complete
process1.join()
process2.join()
print("Done!")Using asyncio Module
The asyncio module is for asynchronous code: coroutines that can be paused and resumed, a natural fit for I/O-bound tasks.
import asyncio
async def print_numbers():
for i in range(1, 6):
await asyncio.sleep(1)
print(f"Number: {i}")
async def print_letters():
for letter in 'abcde':
await asyncio.sleep(1.5)
print(f"Letter: {letter}")
async def main():
task1 = asyncio.create_task(print_numbers())
task2 = asyncio.create_task(print_letters())
await task1
await task2
asyncio.run(main())