Software Engineering basics - Concurrency and Parallelism

Concurrency and Parallelism

Concurrency and Parallelism are approaches in software design that allow programs to perform multiple operations at the same time, improving efficiency, responsiveness, and performance.


Concurrency

  • Definition: The ability of a system to handle multiple tasks at once by interleaving their execution (not necessarily at the exact same time).

  • Key Idea: Multiple tasks make progress independently and may share resources.

  • Example: A web server handling multiple client requests by switching between them quickly.


Parallelism

  • Definition: The simultaneous execution of multiple tasks to speed up computation.

  • Key Idea: Tasks run at the same time on multiple processors/cores.

  • Example: Performing large-scale matrix multiplication by dividing work across CPU cores or GPUs.


Concurrency vs. Parallelism

Aspect Concurrency Parallelism
Focus Managing multiple tasks at once Executing multiple tasks simultaneously
Hardware Need Can run on a single core (task switching) Requires multiple cores/processors
Example Multithreaded UI app (smooth scrolling + typing) Scientific computing, GPU acceleration

Techniques for Concurrency & Parallelism

  1. Multithreading – Multiple threads within a program share resources.

  2. Multiprocessing – Using multiple CPU processes for true parallel execution.

  3. Asynchronous Programming – Non-blocking execution (e.g., async/await in Python or JavaScript).

  4. Distributed Computing – Running tasks across multiple machines (e.g., cloud computing, Hadoop, Spark).


Example in Python (Concurrency vs Parallelism):

import threading, multiprocessing, time

def task(name):
    print(f"Task {name} starting")
    time.sleep(2)
    print(f"Task {name} done")

# Concurrency (Threads: share same memory, switch tasks)
threads = [threading.Thread(target=task, args=(i,)) for i in range(2)]
for t in threads: t.start()
for t in threads: t.join()

# Parallelism (Processes: run in parallel on multiple cores)
processes = [multiprocessing.Process(target=task, args=(i,)) for i in range(2)]
for p in processes: p.start()
for p in processes: p.join()

Benefits

  • Better performance on multi-core systems.

  • Improved responsiveness (e.g., apps don’t freeze during heavy tasks).

  • Faster processing for large-scale data and computations.