Threading Module in Python

Threading

Let us explore the Threading module. Firstly let’s look at an example of how much time a code takes to run when we do not use threading and when we do use threading.

import time

# starting a counter
start = time.perf_counter()

def sleeper_function(sleep_length):
    print("Going to sleep now:")
    time.sleep(sleep_length)
    print("Finished sleeping")

# without threads
sleeper_function(1)
sleeper_function(1)

# ending a counter
stop = time.perf_counter()

print(f"Total time taken: {round(stop-start, 3)} secs")  # rounding it to 3 digits

The time taken to run is:

Let’s consider the time taken for the same code but when we use threads.

# with threads
time_1 = threading.Thread(target=sleeper_function, args=[1])
time_2 = threading.Thread(target=sleeper_function, args=[1])



time_1.start()
time_2.start()

Let’s break down the above code, particularly the code that uses threads. Consider the entire code when we use threads.

import threading
import time

# starting a counter
start = time.perf_counter()

def sleeper_function(sleep_length):
    print("Going to sleep now:")
    time.sleep(sleep_length)
    print("Finished sleeping")

# with threads
time_1 = threading.Thread(target=sleeper_function, args=[1])
time_2 = threading.Thread(target=sleeper_function, args=[1])

# starting our threads
time_1.start()
time_2.start()

# adding to main thread
time_1.join()
time_2.join()


# ending a counter
stop = time.perf_counter()

print(f"Total time taken: {round(stop-start, 3)} secs")  # rounding it to 3 digits

The time module offers a function called “perf_counter()”, which we can use to start a counter. So we create 2 variables to monitor the start and end of the counter, and the time elapsed will be the difference between these 2 variables. The value returned is float and a large decimal number, thus we round it to three digits after the decimal.

As we discussed in the previous blog, the sleep function halts the flow of a program for a specified number of seconds. We then created two objects of the class Thread (from the threading module) and gave them two arguments. Firstly, the name of the function we want our thread to use, and secondly the arguments we want to use for that particular function. To start a thread, we simply need to call the start() function.

We then called the join function for each of the individual threads. The necessity of adding the join function is that each of the threads gets added to the main thread. If that did not make much sense let’s run this code without the join() function and see the output.

As we can see before the threads could finish their execution the main thread (or script) has come to an end. To make sure that the main thread before completion, waits for all other threads to come to an end, we need to use the join() function for each of those threads.

Functions of the Thread Module

Let’s consider a few functions from the Thread module. The first one is the activeCount() function, which as the name indicates results in the active number of threads within a script (aka Python file).

import threading, time

def sleeper_function(sleep_length):
    print("Going to sleep now:")
    time.sleep(sleep_length)
    print("Finished sleeping")

time_1 = threading.Thread(target=sleeper_function, args=[1])
time_2 = threading.Thread(target=sleeper_function, args=[1])

time_1.start()
time_2.start()

print(f"There are {threading.activeCount()} active threads")

time_1.join()
time_2.join()

print("Script has ended")

When we run this, we get:

NOTE: We see there are 3 active threads, the first two are the threads we created and the third one is the main thread.

 

The second function is enumerate() which lists all of the active threads in our script.

import threading, time

def sleeper_function(sleep_length):
    print("Going to sleep now:")
    time.sleep(sleep_length)
    print("Finished sleeping")

time_1 = threading.Thread(target=sleeper_function, args=[1])
time_2 = threading.Thread(target=sleeper_function, args=[1])

time_1.start()
time_2.start()

print("The active threads area:")
print(threading.enumerate())

time_1.join()
time_2.join()

print("Script has ended")

We get the output as:

The third function is current_thread(), which returns which object of the Thread is currently active.

import threading, time

def sleeper_function(sleep_length):
    print("Going to sleep now:")
    time.sleep(sleep_length)
    print(f"The currently active thread is: {threading.current_thread()}")
    print("Finished sleeping")


time_1 = threading.Thread(target=sleeper_function, args=[1])
time_2 = threading.Thread(target=sleeper_function, args=[1])

time_1.start()
time_2.start()

print(f"The currently active thread is: {threading.current_thread()}")

time_1.join()
time_2.join()

print("Script has ended")

Consider the output of this program, notice how the main thread comes before the two threads we created. This is because the print statement of the main thread is before the join() functions of our threads, and so the main thread continues to be executed till it encounters the join() functions. The individual threads are only visible within the target functions.

 

What have we learned?

  • How to initiate a counter using the time module?
  • How do we create a thread using the “threading” module?
  • What are the parameters of the Thread object?
  • What is the main thread?
  • What is the use of the join() function?
  • What are some of the functions we can use from the “threading” module?
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments