Sobes.tech
Junior

What is concurrency?

sobes.tech AI

Answer from AI

Concurrency is the ability of a system to process multiple tasks simultaneously, achieving efficiency and responsiveness. In Python, this is achieved using threads and processes.

Main approaches:

  1. Multithreading: Threads run within the same address space of a process. Suitable for I/O-bound tasks (waiting for input/output). Due to GIL (Global Interpreter Lock), it is inefficient for CPU-bound tasks, as GIL limits the parallel execution of Python code on multi-core processors.

    import threading
    import time
    
    def worker(num):
        """Thread worker function"""
        print(f'Worker {num} starting')
        time.sleep(1)
        print(f'Worker {num} finishing')
    
    threads = []
    for i in range(5):
        t = threading.Thread(target=worker, args=(i,))
        threads.append(t)
        t.start()
    
    for t in threads:
        t.join()
    
    print('All threads finished')
    
  2. Multiprocessing: Processes run in separate address spaces. Each process has its own Python interpreter and stack. Effective for CPU-bound tasks, as GIL is not a limitation between processes. Requires more resources (memory, CPU) because a new process must be created for each task.

    import multiprocessing
    import time
    
    def worker(num):
        """Process worker function"""
        print(f'Worker {num} starting')
        time.sleep(1)
        print(f'Worker {num} finishing')
    
    if __name__ == "__main__":
        processes = []
        for i in range(5):
            p = multiprocessing.Process(target=worker, args=(i,))
            processes.append(p)
            p.start()
    
        for p in processes:
            p.join()
    
        print('All processes finished')
    
  3. Asyncio: Uses a single thread to perform multiple I/O operations by switching between tasks (coroutines) during I/O wait times. Suitable for highly concurrent I/O-bound tasks.

    import asyncio
    import time
    
    async def worker(num):
        """Async worker function"""
        print(f'Worker {num} starting')
        await asyncio.sleep(1)
        print(f'Worker {num} finishing')
    
    async def main():
        tasks = []
        for i in range(5):
            task = asyncio.create_task(worker(i))
            tasks.append(task)
    
        await asyncio.gather(*tasks)
    
        print('All tasks finished')
    
    if __name__ == "__main__":
        asyncio.run(main())
    

Comparison table:

Characteristic Multithreading Multiprocessing Asyncio
Applicability I/O-bound CPU-bound I/O-bound
Parallelism Pseudo-parallelism (on CPU) True parallelism Concurrency on a single thread
CPU usage Limited by GIL Uses all cores Single core
Memory Shared Separate Shared
Complexity Moderate Higher Higher (requires async/await)
Communication Easy (shared memory) IPC (Queue, Pipe) Easy (shared memory)

The choice of approach depends on the nature of the task (CPU-bound vs I/O-bound) and concurrency requirements.