Multithreading in Python | Set 1

Counters | Python Methods and Functions

Let's first understand the concept of thread in computer architecture.


In computing, a process is an instance of a computer program that is running. Any process has 3 main components:

  • An executable program.
  • Associated data required by the program (variables, workspace, buffers, etc.)
  • State of process

Stream — it is an object within a process that can be scheduled to run. It is also the smallest unit of processing that can be done in the OS (operating system).

Simply put, stream — it is a sequence of such instructions in a program that can be executed independently of other code. For simplicity, you can assume that the stream — it's just a subset of a process!

A thread contains all this information in a (TCB) thread control block :

  • Thread ID : A unique identifier (TID) is assigned to each new thread
  • Stack Pointer: Points to the stack of a thread in a process. The stack contains local variables in the scope of the thread.
  • Program counter: the register that stores the address of the instruction currently being executed by the thread.
  • Thread status: can be running, ready, pending, starting, or ready.
  • Set of thread registers : registers assigned to a thread for computation.
  • Parent Process Pointer: Pointer to the Process Control Block (PCB) of the process that the thread is in.

Consider the diagram below, to understand the relationship between a process and its thread:


Multiple threads can exist in a single process, where:

  • Each thread contains its own set registers and local lane Small (stored on the stack) .
  • All threads in a process share global variables (stored on the heap) and code .

Consider the diagram below to understand how multiple threads exist in memory:

Multithreading is defined as the ability of a processor to execute multiple threads at the same time.

In a simple, single-core CPU , it is achieved using frequent switching between threads. This is termed as context switching . In context switching, the state of a thread is saved and state of another thread is loaded whenever any interrupt (due to I / O or manually set) takes place. Context switching takes place so frequently that all the threads appear to be running parallely (this is termed as multitasking).

Consider the diagram below, in which the process contains two active streams:

Python Multithreading

In Python, the Threads module provides a very simple and intuitive API for creating multiple threads in a program.

Let's look at a simple example of using the streams module:

# Python program to illustrate the concept
Number of streams
# importing the streams module

import threading


def print_cube (num):

"" "

  function for printing a cube with a given number

"" "

  print ( "Cube: {}" . format (num * num * num))


def print_square (num):

"" "

  function for printing a square of a given number

"" "

  print ( " Square: {} " . format (num * num))


if __ name__ = = "__ main__" :

# create stream

t1 = threading.Thread (target = print_square, args = ( 10 ,))

t2 = threading.Thread (target = print_cube, args = ( 10 ,))


# starting theme 1

t1.start ()

  # Starter Topic 2

t2 .start ()


# wait for stream 1 to end

t1.join ()

  # wait for stream 2 to complete

t2.join ()


# both streams are complete

print ( "Done!" )

 Square: 100 Cube: 1000 Done! 

Let's try to understand the above code:

  • To import the threading module, we do:
     import threading 
  • To create new thread, we create an object of class Thread . The following arguments are required:
    • target : function to be executed by the thread
    • args : arguments to pass to the target function

    In the above example, we created 2 threads with different target functions:

     t1 = threading.Thread (target = print_square, args = (10,)) t2 = threading.Thread (target = print_cube, args = (10,)) 
  • To start a thread, we use the start method of the Thread class.
     t1. start () t2.start () 
  • As soon as the threads start, the current program (you can think of it as the main thread) continues to run as well. To stop the execution of the current program before the thread finishes, we use the join method.
     t1.join () t2.join () 

    As a result, the current program will first wait for completion t1, and then t2 . Once completed, the rest of the current program is executed.

Consider the diagram below for a better understanding of how the above program works:

Consider the below python program in which we print the thread name and the corresponding process for each task :

# Python program to illustrate the concept
Number of threads

import threading

import os


def task1 ():

print ( "Task 1 assigned to thread: {}" . format (threading.current_thread (). name))

print ( "ID of process running task 1: {}" . format (os.getpid ()))


def task2 () :

print ( "Task 2 assigned to thread: {}" . format (threading.current_thread (). name))

print ( "ID of process running task 2: {}" . format (os.getpid ()))


if __ name__ = = "__ main__" :


  # print the id of the current process

print ( "ID of process running main program: {} " . format (os. getpid ()))


# print main stream name

  print ( " Main thread name: {} " . format (threading.main_thread (). name))


# creating themes

  t1 = threading.Thread (target = task1, name = ' t1' )

t2 = threading.Thread (target = task2, nam e = 't2'


# starting topics

t1.start ()

  t2.start ()


# wait for all streams to end

t1.join ()

t2.join ()

 ID of process running main program: 11758 Main thread name: MainThread Task 1 assigned to thread: t1 ID of process running task 1: 11758 Task 2 assigned to thread: t2 ID of process running task 2: 11758 

Let's p let's try to understand the above code:

  • We use the os.getpid () function to get the ID of the current process.
     print ("ID of process running main program : {} ". format (os.getpid ())) 

    As you can see from the output, the process ID remains the same for all threads.

  • We use the threading.main_thread (), to get the main thread object. Under normal conditions, the main thread is the thread from which the Python interpreter was started. The name attribute of the thread object is used to get the name of the thread.
     print ("Main thread name: {}". Format (threading.main_thread (). Name)) 
  • We use the threading.current_thread () function to get the current thread object.
     print ("Task 1 assigned to thread: {}". format (threading.current_thread (). name) ) 

The diagram below clarifies the above concept:

So this was a quick introduction to multithreading in Python. The next article in this series focuses on cross-thread synchronization .