Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?

Multi-Threaded Programs in Python Using threading Module

You could have heard the phrases “parallelization” or “concurrency“, which consult with scheduling duties to run parallelly or concurrently (on the identical time) to avoid wasting time and sources. This can be a frequent apply in asynchronous programming, the place coroutines are used to execute duties concurrently.

Threading in Python is used to run a number of duties on the identical time, therefore saving time and sources and growing effectivity.

Though multi-threading can save time and sources by executing a number of duties on the identical time, utilizing it in code can result in security and reliability points.

On this article, you will be taught what’s threading in Python and the way you should use it to make a number of duties run concurrently.



What’s Threading?

Threading, as beforehand acknowledged, refers back to the concurrent execution of a number of duties in a single course of. That is completed by using Python’s threading module.

Threads are smaller items of this system that run concurrently and share the identical reminiscence house.



The right way to Create Threads and Execute Concurrently

Python supplies a module referred to as threading that gives a high-level threading interface to create and handle threads in Python applications.



Create and Begin Thread

A thread could be created utilizing the Thread class supplied by the threading module. Utilizing this class, you possibly can create an occasion of the Thread after which begin it utilizing the .begin() technique.

import threading

# Creating Goal Operate
def num_gen(num):
    for n in vary(num):
        print("Thread: ", n)

# Principal Code of the Program
if __name__ == "__main__":
    print("Assertion: Creating and Beginning a Thread.")
    thread = threading.Thread(goal=num_gen, args=(3,))
    thread.begin()
    print("Assertion: Thread Execution Completed.")
Enter fullscreen mode

Exit fullscreen mode

A thread is created by instantiating the Thread class with a goal parameter that takes a callable object on this case, the num_gen operate, and an args parameter that accepts a listing or tuple of arguments, on this case, 3.

Which means that you’re telling Thread to run the num_gen() operate and move 3 as an argument.

Should you run the code, you will get the next output:

Assertion: Creating and Beginning a Thread.
Assertion: Thread Execution Completed.
Thread:  0
Thread:  1
Thread:  2
Enter fullscreen mode

Exit fullscreen mode

You’ll be able to discover that the Assertion part of the code has completed earlier than the Thread did. Why does this occur?

The thread begins executing concurrently with the primary program and the primary program doesn’t look forward to the thread to complete earlier than persevering with its execution. That is why the above code resulted in executing the print assertion earlier than the thread was completed.

To know this, it is advisable perceive the execution circulation of this system:

  • First, the "Assertion: Creating and Beginning a Thread." print assertion is executed.

  • Then the thread is created and began utilizing thread.begin().

  • The thread begins executing concurrently with the primary program.

  • The "Assertion: Thread Execution Completed." print assertion is executed by the primary program.

  • The thread continues and prints the output.

The thread and the primary program run independently that is why their execution order isn’t fastened.



be part of() Methodology – The Saviour

Seeing the above scenario, you may need thought then learn how to droop the execution of the primary program till the thread is completed executing.

Effectively, the be part of() technique is utilized in that scenario, it would not let e*xecute the code additional till the present thread terminates*.

import threading

# Creating Goal Operate
def num_gen(num):
    for n in vary(num):
        print("Thread: ", n)

# Principal Code of the Program
if __name__ == "__main__":
    print("Assertion: Creating and Beginning a Thread.")
    thread = threading.Thread(goal=num_gen, args=(3,))
    thread.begin()
    thread.be part of()
    print("Assertion: Thread Execution Completed.")
Enter fullscreen mode

Exit fullscreen mode

After creating and beginning a thread, the be part of() technique known as on the Thread occasion (thread). Now run the code, and you will get the next output.

Assertion: Creating and Beginning a Thread.
Thread:  0
Thread:  1
Thread:  2
Assertion: Thread Execution Completed.
Enter fullscreen mode

Exit fullscreen mode

As could be seen, the "Assertion: Thread Execution Completed." print assertion is executed after the thread terminates.



Daemon Threads

Daemon threads run within the background and terminate instantly whether or not they accomplished the work or not when the primary program exits.

You can also make a daemon thread by passing the daemon parameter when instantiating the Thread class. You’ll be able to move a boolean worth to point whether or not the thread is a daemon (True) or not (False).

import threading
import time

def daemon_thread():
    whereas True:
        print("Daemon thread is working.")
        time.sleep(1)
        print("Daemon thread completed executing.")

if __name__ == "__main__":
    thread1 = threading.Thread(goal=daemon_thread, daemon=True)

    thread1.begin()
    print("Principal program exiting.")
Enter fullscreen mode

Exit fullscreen mode

A thread is created by instantiating the Thread class passing the daemon_thread operate inside it and to mark it as a daemon thread, the daemon parameter is about to True.

The daemon_thread() operate is an infinite loop that prints a press release, sleeps for one second, after which once more prints a press release.

Now if you run the above code, you will get the next output.

Daemon thread is working.Principal program exiting.
Enter fullscreen mode

Exit fullscreen mode

You’ll be able to see that as quickly as the primary program exits, the daemon thread terminates.

On the time when the daemon_thread() operate enters the loop, the concurrently working principal program exits, and the daemon_thread() operate by no means reaches the following print assertion as could be seen within the output.



threading.Lock – Avoiding Race Situations

Threads, as , run concurrently in a program. In case your program has a number of threads, they could share the identical sources or the important part of the code on the identical time, one of these situation known as race circumstances.

That is the place the Lock comes into play, it acts like a synchronization barrier that stops a number of threads from accessing the actual code or sources concurrently.

The thread calls the purchase() technique to accumulate the Lock and the launch() technique to launch the Lock.

import threading

# Creating Lock occasion
lock = threading.Lock()

information = ""

def read_file():
    international information
    with open("pattern.txt", "r") as file:
        for data in file:
            information += "n" + data

def lock_task():
    lock.purchase()
    read_file()
    lock.launch()

if __name__ == "__main__":
    thread1 = threading.Thread(goal=lock_task)
    thread2 = threading.Thread(goal=lock_task)

    thread1.begin()
    thread2.begin()

    thread1.be part of()
    thread2.be part of()

    # Printing the information learn from the file
    print(f"Information: {information}")
Enter fullscreen mode

Exit fullscreen mode

First, a Lock is created utilizing the threading.Lock() and retailer it contained in the lock variable.

An empty string is created (information) for storing the data from each threads concurrently.

The read_file() operate is created that reads the data from the pattern.txt file and provides it to the information.

The lock_task() operate is created and when it’s referred to as, the next occasions happen:

  • The lock.purchase() technique will purchase the Lock instantly when the lock_task() operate known as.

  • If the Lock is out there, this system will execute the read_file() operate.

  • After the read_file() operate completed executing, the lock.launch() technique will launch the Lock to make it out there once more for different threads.

Inside the if __name__ == "__main__" block, two threads are created thread1 and thread2 that each runs the lock_task() operate.

Each threads run concurrently and try and entry and execute the read_file() operate on the identical time however just one thread can entry and enter the read_file() at a time because of the Lock.

The primary program waits for each threads to execute utterly due to thread1.be part of() and thread2.be part of().

Then utilizing the print assertion, the data current within the file is printed.

Information: 
Hey there! Welcome to GeekPython.
Hey there! Welcome to GeekPython.
Enter fullscreen mode

Exit fullscreen mode

As could be seen within the output, one thread at a time reads the file. Nonetheless, there have been two threads that is why the file was learn two occasions, first by thread1 after which by thread2.



Semaphore Objects in Threading

Semaphore lets you restrict the variety of threads that you just need to entry the shared sources concurrently. Semaphore has two strategies:

  • purchase(): Thread can purchase the semaphore whether it is out there. When a thread acquires a semaphore, the semaphore’s rely decrement whether it is better than zero. If the rely is zero, the thread waits till the semaphore is out there.

  • launch(): After utilizing the sources, the thread releases the semaphore that ends in an increment within the rely. Which means that shared sources can be found.

Semaphore is used to restrict entry to shared sources, stopping useful resource exhaustion and making certain managed entry to sources with restricted capability.

import threading

# Making a semaphore
sem = threading.Semaphore(2)

def thread_task(num):
    print(f"Thread {num}: Ready")

    # Purchase the semaphore
    sem.purchase()
    print(f"Thread {num}: Acquired the semaphore")

    # Simulate some work
    for _ in vary(5):
        print(f"Thread {num}: In course of")

    # Launch the semaphore when achieved
    sem.launch()
    print(f"Thread {num}: Launched the semaphore.")

if __name__ == "__main__":
    thread1 = threading.Thread(goal=thread_task, args=(1,))
    thread2 = threading.Thread(goal=thread_task, args=(2,))
    thread3 = threading.Thread(goal=thread_task, args=(3,))

    thread1.begin()
    thread2.begin()
    thread3.begin()

    thread1.be part of()
    thread2.be part of()
    thread3.be part of()

    print("All threads have completed.")
Enter fullscreen mode

Exit fullscreen mode

Within the above code, Semaphore is instantiated with the integer worth of 2 which implies two threads are allowed to run on the identical time.

Three threads are created and all of them use the thread_task() operate. However solely two threads are allowed to run on the identical time, so two threads will entry and enter the thread_task() operate on the identical time, and when any of the threads releases the semaphore, the third thread will purchase the semaphore.

Thread 1: Ready
Thread 1: Acquired the semaphore
Thread 1: In course of
Thread 1: In course of
Thread 1: In course of
Thread 1: In course of
Thread 1: In processThread 2: Ready
Thread 2: Acquired the semaphore

Thread 1: Launched the semaphore.
Thread 2: In course of
Thread 2: In course of
Thread 3: WaitingThread 2: In course of
Thread 3: Acquired the semaphore
Thread 3: In course of

Thread 2: In course of
Thread 2: In course of
Thread 2: Launched the semaphore.
Thread 3: In course of
Thread 3: In course of
Thread 3: In course of
Thread 3: In course of
Thread 3: Launched the semaphore.
All threads have completed.
Enter fullscreen mode

Exit fullscreen mode



Utilizing ThreadPoolExecutor to Execute Duties from a Pool of Employee Threads

The ThreadPoolExecutor is part of concurrent.options module that’s used to execute a number of duties concurrently. Utilizing ThreadPoolExecutor, you possibly can run a number of duties or capabilities concurrently with out having to manually create and handle threads.

from concurrent.futures import ThreadPoolExecutor

# Creating pool of 4 threads
executor = ThreadPoolExecutor(max_workers=4)

# Operate to guage sq. quantity
def square_num(num):
    print(f"Sq. of {num}: {num * num}.")

task1 = executor.submit(square_num, 5)
task2 = executor.submit(square_num, 2)
task3 = executor.submit(square_num, 55)
task5 = executor.submit(square_num, 4)

# Look forward to duties to finish after which shutdown
executor.shutdown()
Enter fullscreen mode

Exit fullscreen mode

The above code creates a ThreadPoolExecutor with a most of 4 employee threads which implies the thread pool can have a most of 4 employee threads executing the duties concurrently.

4 duties are submitted to the ThreadPoolExecutor utilizing the submit technique with the square_num() operate and varied arguments. It will execute the operate with specified arguments and prints the output.

In the long run, the shutdown technique known as, in order that ThreadPoolExecutor shutdowns after the duties are accomplished and sources are freed.

You do not have to explicitly name the shutdown technique when you create ThreadPoolExecutor utilizing the with statement.

from concurrent.futures import ThreadPoolExecutor

# Activity
def square_num(num):
    print(f"Sq. of {num}: {num * num}.")

# Utilizing ThreadPoolExecutor as context supervisor
with ThreadPoolExecutor(max_workers=4) as executor:
    task1 = executor.submit(square_num, 5)
    task2 = executor.submit(square_num, 2)
    task3 = executor.submit(square_num, 55)
    task5 = executor.submit(square_num, 4)
Enter fullscreen mode

Exit fullscreen mode

Within the above code, the ThreadPoolExecutor is used with the with assertion. When the with block is exited, the ThreadPoolExecutor is robotically shut down and its sources are launched.

Each codes will produce the identical outcome.

Sq. of 5: 25.
Sq. of two: 4.
Sq. of 55: 3025.
Sq. of 4: 16.
Enter fullscreen mode

Exit fullscreen mode



Frequent Operate in Threading

The threading module supplies quite a few capabilities and a few of them are defined beneath.



Getting Principal and Present Thread

The threading module has a main_thread() and a current_thread() operate which is used to get the primary thread and the at the moment working thread respectively.

import threading

def process():
    for _ in vary(2):
        # Getting the present thread identify
        print(f"Present Thread: {threading.current_thread().identify} is working.")

# Getting the primary thread identify
print(f"Principal thread   : {threading.main_thread().identify} began.")
thread1 = threading.Thread(goal=process)
thread2 = threading.Thread(goal=process)

thread1.begin()
thread2.begin()

thread1.be part of()
thread2.be part of()
print(f"Principal thread   : {threading.main_thread().identify} completed.")
Enter fullscreen mode

Exit fullscreen mode

As a result of the main_thread() and current_thread() capabilities return a Thread object, threading.main_thread().identify is used to get the identify of the primary thread and threading.current_thread().identify is used to get the identify of the present thread.

Principal thread   : MainThread began.
Present Thread: Thread-1 (process) is working.
Present Thread: Thread-1 (process) is working.
Present Thread: Thread-2 (process) is working.
Present Thread: Thread-2 (process) is working.
Principal thread   : MainThread completed.
Enter fullscreen mode

Exit fullscreen mode



Monitoring At the moment Lively Threads

The threading.enumerate() operate is used to return the listing of Thread objects which might be at the moment working. This contains the primary thread even whether it is terminated and excludes terminated threads and threads that haven’t began but.

If you wish to get the variety of Thread objects which might be at the moment alive, you possibly can make the most of the threading.active_count() operate.

import threading

def process():
    print(f"Present Thread     : {threading.current_thread().identify} is working.")

# Getting the primary thread identify
print(f"Principal thread        : {threading.main_thread().identify} began.")

threads_list = []

for _ in vary(5):
    thread = threading.Thread(goal=process)
    thread.begin()
    threads_list.append(thread)
    # Getting the lively thread rely
    print(f"nLively Thread Depend: {threading.active_count()}")

for thread in threads_list:
    thread.be part of()

print(f"Principal thread        : {threading.main_thread().identify} completed.")
# Getting the lively thread rely
print(f"Lively Thread Depend: {threading.active_count()}")
# Getting the listing of lively threads
for lively in threading.enumerate():
    print(f"Lively Thread Listing: {lively.identify}")
Enter fullscreen mode

Exit fullscreen mode

Output

Principal thread        : MainThread began.
Present Thread     : Thread-1 (process) is working.
Lively Thread Depend: 2

Present Thread     : Thread-2 (process) is working.
Lively Thread Depend: 2

Present Thread     : Thread-3 (process) is working.
Lively Thread Depend: 2

Present Thread     : Thread-4 (process) is working.

Lively Thread Depend: 2
Present Thread     : Thread-5 (process) is working.

Lively Thread Depend: 1
Principal thread        : MainThread completed.
Lively Thread Depend: 1
Lively Thread Listing: MainThread
Enter fullscreen mode

Exit fullscreen mode



Getting Thread Id

import threading
import time

def process():
    print(f"Thread {threading.get_ident()} is working.")
    time.sleep(1)
    print(f"Thread {threading.get_ident()} is terminated.")

print(f"Principal thread began.")

threads_list = []

for _ in vary(5):
    thread = threading.Thread(goal=process)
    thread.begin()
    threads_list.append(thread)

for thread in threads_list:
    thread.be part of()

print(f"Principal thread completed.")
Enter fullscreen mode

Exit fullscreen mode

Each thread working in a course of is assigned an identifier and the threading.get_ident() operate is used to retrieve the identifier of the at the moment working thread.

Principal thread began.
Thread 9824 is working.
Thread 7188 is working.
Thread 4616 is working.
Thread 3264 is working.
Thread 7716 is working.
Thread 7716 is terminated.
Thread 9824 is terminated.
Thread 7188 is terminated.Thread 4616 is terminated.

Thread 3264 is terminated.
Principal thread completed.
Enter fullscreen mode

Exit fullscreen mode



Conclusion

A thread is a smaller unit in this system that’s created utilizing the threading module in Python. Threads are duties or capabilities that you should use a number of occasions in your program to execute concurrently to avoid wasting time and sources.

On this article, you’ve got realized:

  • What’s threading and the way do you create and begin a thread

  • Why be part of() technique is used

  • What are daemon threads and learn how to create one

  • The right way to Lock threads to keep away from race circumstances

  • How semaphore is used to restrict the variety of threads that may entry the shared sources on the identical time.

  • How one can execute a gaggle of duties utilizing the ThreadPoolExecutor with out having to create threads.

  • Some frequent capabilities supplied by the threading module.


๐Ÿ†Different articles you is perhaps desirous about when you appreciated this one

โœ…Comparing the accuracy of 4 pre-trained deep learning models?

โœ…What are coroutines in Python and how do use them in asynchronous programming?

โœ…Async/Await in Python using the asyncio module?

โœ…How to structure a Flask app using Flask Blueprint?

โœ…Upload and display images on the frontend using Flask in Python.

โœ…How to connect the SQLite database with the Flask app using Python?


That is all for now

Maintain CodingโœŒโœŒ

Add a Comment

Your email address will not be published. Required fields are marked *

Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?