Multithreading and Multiprocessing in Python

To begin with, let us clear up some terminlogy:

  • Concurrency is when two or more tasks can start, run, and complete in overlapping time periods. It doesn’t necessarily mean they’ll ever both be running at the same instant. Eg. multitasking on a single-core machine.
  • Parallelism is when two or more tasks are executed simultaneously.
  • A thread is a sequence of instructions within a process. It can be thought of as a lightweight process. Threads share the same memory space.
  • A process is an instance of a program running in a computer which can contain one or more threads. A process has its independent memory space.

The threading module is used for working with threads in Python.

The CPython implementation has a Global Interpreter Lock (GIL) which allows only one thread to be active in the interpreter at once. This means that threads cannot be used for parallel execution of Python code. While parallel CPU computation is not possible, parallel IO operations are possible using threads. This is because performing IO operations releases the GIL. To learn more about the GIL refer here.

What are threads used for in Python?

  • In GUI applications to keep the UI thread responsive
  • IO tasks (network IO or filesystem IO)

Threads should not be used for CPU bound tasks. Using threads for CPU bound tasks will actually result in worse performance compared to using a single thread.

The following example demonstrates the use of threads for network IO using the libraryrequests. This is a toy example use case of threads for networking IO.

Single thread: 4 seconds

Two threads : 3 seconds

Five threads: 2 seconds

In network IO, most of the time is spent waiting for the response from the URL, so this is another use case where using threads improves performance.

Let me demonstrate why it’s a bad idea to use threads for CPU bound tasks. In the following program, a queue holds numbers. The task is to find the sum of a prime number less than or equal to the given number. This is clearly a CPU bound task.

Single thread: 25.5 seconds

Two threads: 28 seconds

The results are very clear: don’t use threads to improve the performance of CPU bound tasks. You will always end up with worse performance.

For the parallel execution of tasks, the modulemultiprocessing can be used.

In the following example, we take the same task used above and process the inputs in parallel using the multiprocessing module.

Using a single process : 27 seconds

Using two processes : 19 seconds

Using three processes : 18 seconds

We see a huge improvement from using a single process to using two processes. But the jump from two processes to three processes is minimal. The reason behind this is my hardware. I have a dual core (laptop) CPU with hyperthreading (the OS detects it as four CPUs due to hyperthreading). On a side note, I knew Intel’s hyperthreading was not a replacement for more cores. The above example is a noob verification.

So using the multiprocessing module results in the full utilization of the CPU.

Inter process communication can be achieved using queues or pipes. The Queue in the multiprocessingmodule works similar to the queue module used to demonstrate how the threading module works so I won’t cover it again.

Another useful communication mechanism between processes is a pipe. A pipe is a duplex (two way) communication channel. Note : Reading or writing to the same end of the pipe simultaneously can result in data corruption.

The following is a basic example:


Useful Links: