Multithreading

Multithreading is a process of executing multiple threads parallelly is called multithreading. The Multi threads live in the same process in the same space

Thread

thread is a sequence of instructions in a program that executes independently. Threads are considered to be useful because they do not require much memory usage if we compare them to a new process. Generally, thread three states: a start state, an execution state, and a finished state. While the thread is running, we can interrupt before it ends or we can put it in a sleep state, also called yielding. The thread is a low-level library, we will use the threading library is much simpler to use.

Starting a thread

To start a thread we need to call threading.Thread(target=function). This method allows us to start a child thread that runs a specific code. Let’s see the next example:

 

import threading
from time import sleep
def print_strings():
    sleep(0.5)
    for i in range(0,7):
        print("Print number: " + str(i + 1))
        sleep(1.7)

def print_thread():
    for i in range(0,9):
        print("Thread print")
        sleep(1)
thread = threading.Thread(target=print_thread)
try:
    thread.start()
    print_strings()
except:
    print("Unable to start thread")

 

Output:

Thread print
Print number: 1
Thread print
Thread print
Print number: 2
Thread print
Print number: 3
Thread print
Thread print
Print number: 4
Thread print
Thread print
Print number: 5
Thread print
Print number: 6
Print number: 7


In the example above, we create two functions with time.sleep module to give a delay between the prints. We create a thread by calling the target for thread the print_thread function created. After that, we start the thread and the print_strings function and you will be able to see that these two functions are running at the same time, one is running in the parent process and the other one is running in the child process (thread).

Synchronizing threads

Sometimes you need to synchronize threads in order to assure that all steps goes in a specific order. For synchronizing threads, lock() method and the release() method are used. This way you can assure that you control which thread is running and which thread is waiting. Let’s take a look at the next example:

 

import threading
from time import sleep
def thread_print(name):
    threadLock.acquire()
    for i in range(0,7):
        print(str(name) + ": number " + str(i))
        sleep(1)
    threadLock.release()
threadLock = threading.Lock()
threads_list = []
thread_1 = threading.Thread(target=thread_print, args=("Thread1",))
thread_2 = threading.Thread(target=thread_print, args=("Thread2",))
thread_1.start()
thread_2.start()
threads_list.append(thread_1)
threads_list.append(thread_2)
for thread in threads_list:
    thread.join()
print("All threads done...")

 

Output:

Thread1: number 0
Thread1: number 1
Thread1: number 2
Thread1: number 3
Thread1: number 4
Thread1: number 5
Thread1: number 6
Thread2: number 0
Thread2: number 1
Thread2: number 2
Thread2: number 3
Thread2: number 4
Thread2: number 5
Thread2: number 6
All threads done...


In this example, first, we create a thread function where it acquires the thread and releases the thread once it finishes the process. We create a variable thread-lock, that uses threading.Lock() method to use for the methods acquire and release on the function thread_print. We create a threads list in order to create a wait statement until all threads are done. Then, we create two variables, thread_1 and thread_2 which are two different threads that will be running. After that, we start the two threads at the same time and append both threads to the threads_list. We do a for loop with the join() method to create the wait statement and when the for is done, then it will be printed a message saying “All threads done…”. When you run the code you can see the thread_1 running and when the thread_1 finishes, the thread_2 starts. Both threads started at the same time, but when you apply the lock mechanism on threads, you can accomplish a synchronization in threads.

Programming using multithreading

A thread is a child process from the application that runs a set of instructions. The CPU is responsible to manage the threads on the operating system. Multithreading allows us to handle and to update some of the program components without influencing the parent process but, if we use many threads within a single process we can move towards a bottleneck, which will slow the parent process. In order to avoid the bottleneck, we must use only the threads that we need to run in the background and close them once they are finished, in order to assure that our program is flowing in the right way.

Threading module

We use the threading module above, and this module provides some methods to explore the threads. Besides that, this module is called the new threading modules since it is more powerful and it provides high-level support if we compare to the original thread module. From the threading module we can use the following methods:

There are more methods related to threading, if you want to explore them all you can check the official python documentation on the following website: https://docs.python.org/3/library/threading.html

 

Method

DESCRIPTION

active_count()

Returns the number of threads that are active.

current_thread()

Returns the current thread object that is being called.

excepthook()

Handle any exception raised by Thread.run()

get_ident()

Returns the current thread identifier.

GET_NATIVE_ID()

Returns the current thread native ID assigned by the kernel.

enumerate()

Returns a list of all threads that are running.

main_thread()

Returns the main thread object. The main thread is the thread started by the python interpreter.

settrace(func)

This method sets a trace function for all started threads.

setprofile(func)

This method set a profile function to all threads.

stack_size([size])

Returns the thread stack size used on creating new threads. We can optionally choose the size for the stack size.

TIMEOUT_MAX

Returns the maximum values allowed for the timeout parameter of blocking functions.

start()

Start a thread

RUN()

This method is used in a subclass that represents a thread activity.

join(TIMEOUT=NONE)

This puts a thread into a sleep mode until another thread terminates.

name

Used only for identification purposes.

ident

The same as get_ident(), where it return the thread identifier.

Native_id

The same as get_native_id, where it returns the native thread ID of the selected thread.

is_alive()

Return a Boolean value depending on if the thread is alive or not.

daemon

Boolean value that can be set up in threads. When active, when the program exits, the thread will stop immediately.

iSDAEMON()

Check if thread daemon is active or not.

Related Tutorials