Concurrency vs Parallelism: Understanding the Differences with Examples

Soumyadip Sarkar
3 min readNov 13, 2024

Have you ever wondered how your computer can juggle multiple tasks at once? How it can play music, download files, and let you type an email — all seemingly at the same time? This magic boils down to two fundamental concepts in computer science: concurrency and parallelism. While these terms are often used interchangeably, they represent different ideas. In this article, we’ll demystify concurrency and parallelism, explore their differences, and dive into some code examples to see them in action.

What is Concurrency?

Concurrency is like having a single chef preparing multiple dishes at once in a kitchen. The chef starts chopping vegetables for a salad, then moves to stir the soup before it boils over, and perhaps checks the oven to see if the bread is done. The chef isn’t doing all these tasks at the exact same time but is managing them in a way that they progress together.

In computing terms, concurrency means that an application is making progress on more than one task at the same time by switching between them. It’s about dealing with multiple things at once but not necessarily executing them simultaneously.

Concurrency in Python with Threads

Let’s look at a simple example using Python’s threading module.

import threading
import time

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

thread1 = threading.Thread(target=task, args=('A',))
thread2 = threading.Thread(target=task, args=('B',))

thread1.start()
thread2.start()

thread1.join()
thread2.join()

print("Both tasks are done!")

Explanation:

  • We define a function task that simulates a task taking 2 seconds.
  • We create two threads (thread1 and thread2) that run the task function with different arguments.
  • We start both threads, which allows them to run concurrently.
  • Even though Python’s Global Interpreter Lock (GIL) doesn’t allow true parallel execution of bytecode, threads can still be used for I/O-bound tasks to improve performance through concurrency.

What is Parallelism?

Parallelism is like having multiple chefs in the kitchen, each preparing their own dish at the same time. One chef is chopping vegetables for the salad, another is stirring the soup, and a third is checking the bread in the oven. All tasks are being executed simultaneously.

In computing, parallelism means that multiple tasks are executed at the exact same time, typically using multiple processors or cores.

Parallelism in Python with Multiprocessing

Let’s modify our previous example to use the multiprocessing module.

import multiprocessing
import time

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

if __name__ == '__main__':
process1 = multiprocessing.Process(target=task, args=('A',))
process2 = multiprocessing.Process(target=task, args=('B',))

process1.start()
process2.start()

process1.join()
process2.join()

print("Both tasks are done!")

Explanation:

  • We use multiprocessing.Process to create separate processes.
  • Each process runs independently on a separate CPU core (if available).
  • This allows true parallel execution, suitable for CPU-bound tasks.

Concurrency vs Parallelism: Spotting the Difference

  • Concurrency is about managing multiple tasks at the same time (interleaving), but not necessarily executing them simultaneously.
  • Parallelism is about executing multiple tasks at the exact same time.

Visual Analogy

  • Concurrency: One worker juggling multiple balls, touching one ball at a time but keeping them all in the air.
  • Parallelism: Multiple workers each juggling their own ball simultaneously.

When to Use Concurrency vs Parallelism

  • Concurrency is ideal for I/O-bound tasks where the program spends time waiting for external events (like reading a file or fetching data from the internet).
  • Parallelism is beneficial for CPU-bound tasks that require heavy computations.

Example Scenarios

  • Concurrency: A web server handling multiple client requests by switching between them efficiently.
  • Parallelism: Processing large datasets by splitting the data across multiple processors.

Combining Concurrency and Parallelism

In real-world applications, concurrency and parallelism can be combined to optimize performance.

Asynchronous Programming with asyncio

Python’s asyncio library allows for concurrent code using the async and await syntax.

import asyncio

async def task(name):
print(f"Task {name} started")
await asyncio.sleep(2)
print(f"Task {name} finished")

async def main():
await asyncio.gather(
task('A'),
task('B'),
task('C'),
)

asyncio.run(main())

Explanation:

  • asyncio allows us to run tasks concurrently within a single thread.
  • Tasks yield control during I/O operations (await asyncio.sleep(2)), allowing other tasks to run.

Conclusion

Understanding the difference between concurrency and parallelism is crucial for writing efficient programs. Concurrency is about dealing with lots of things at once, while parallelism is about doing lots of things at the same time. By leveraging the right approach based on the nature of the tasks — whether they’re I/O-bound or CPU-bound — you can significantly improve the performance of your applications.

Try It Yourself!

Experiment with the code examples provided:

  • Modify the number of threads or processes and observe how it affects execution time.
  • Try running CPU-bound tasks (like calculating Fibonacci numbers) with both threading and multiprocessing to see the difference.

Happy coding!

--

--

Soumyadip Sarkar
Soumyadip Sarkar

Written by Soumyadip Sarkar

Independent Researcher & Software Engineer

No responses yet