Threading with Python
Threads are the subpart of the process that can be executed simultaneously and share the process resources.
Threads are the smallest unit that an operating system can schedule.
Python provides two modules for threading implementation.
The thread module is deprecated in Python 3. However, it has been renamed to “_thread” for backward incompatibilities in Python 3.
1. thread module
The Thread module provides a function start_new_thread to fork any execution in a separate thread.
thread.start_new_thread(my_function, args[, kwargs])
my_function will be executed in a separate thread with the arguments specified.
When my_function executes successfully, the thread will silently exit.
If an exception is thrown while executing my_function, firstly stack trace will get printed and then thread will exit
Here is the quick example for the above function.
from _thread import start_new_thread def calculate_square(a): """Calculates the square number of a""" Square = a*a print(square) return square if __name__ == “__main__”: start_new_thread(calculate_square, (9) ) start_new_thread(calculate_square, (10) ) start_new_thread(calculate_square, (11) )
In the above example, four threads are created; the main thread itself and the other three threads executing the calculate_square function with arguments.
Note: The order in which these threads will execute is not specific. Also, the main thread will not wait for the other threads to finish its execution.
2. threading module
Threading module has a Thread class.
To implement a new Thread, following steps are required.
- Create a subclass of threading.Thread
- Override the __init__() method.
- Override the run() method.
After performing the above step, you can create the instance of the class and can start the execution mentioned in the run() method by calling the start() method of the instance.
Note: You can call start() only once.
#!/usr/bin/python import threading import time class myThread (threading.Thread): def __init__(self, id, name): threading.Thread.__init__(self) self.id = id self.name = name def run(self): print("Starting " + self.name) print("Exiting " + self.name) if __name__ == “__main__”: # Create new threads t1 = myThread(101, "T1") t2 = myThread(102, "T2") # Start new Threads t1.start() t2.start() # Tell the main thread to wait for other two threads to complete its execution. t1.join() t2.join() print("Exiting Main Thread")
Note: The order in which these threads will execute is not specific. However, the main thread will wait for the other threads to finish its execution.
3. Threading functions
Following are some of the important functions provided by the threading module.
Return the number of Thread objects currently alive. More…
Return the current Thread object. More…
Lists all Thread objects currently alive. It includes daemonic threads, dummy thread objects, and the main thread. More…
4. Thread Object methods
Following are some of the important methods of the threading.Thread object.
Start calls the run() method and creates the separate stack for the thread. More…
The Method that represents the thread’s activity. All the business logic that the thread needs to execute must be invoked from this method. start() method calls this method for the independent execution of the Thread.
Will make the current Thread wait until the caller Thread’s execution is finished.
timeout decides the number of seconds for which the thread must execute before the context switch.
It is recommended to call is_alive() after join() to decide whether a timeout happened – if the thread is still alive, the join() call timed out and execution of the thread is not finished.
5. Lock objects
Lock objects are used for synchronization of the threads and to control and how the thread access resources in critical region.
The lock can be one of the two states locked or unlocked.
It has two basic methods, acquire() and release(). When the state is unlocked, acquire() changes the state to locked. When the state is locked, acquire() blocks until a call to release() in another thread changes it to unlocked, then the acquire() call resets it to locked and returns.
The release() method must be called in the locked state, else a RuntimeError will be raised.
The method thread.allocate_lock is used to create a new lock object:
lock_object = thread.allocate_lock()
Here is the quick example of the usage of the Lock objects
#!/usr/bin/python import threading import time current_running_threads = 0 lock = _thread.allocate_lock() class myThread (threading.Thread): def __init__(self, id, name): threading.Thread.__init__(self) self.id = id self.name = name def run(self): print("Starting " + self.name) lock.acquire() print(“Lock acquired for thread ” + self.name) Current_running_threads += 1 Print(“lock released for the thread” + self.name) Current_running_threads -= 1 lock.release() if __name__ == “__main__”: # Create new threads t1 = myThread(101, "T1") t2 = myThread(102, "T2") # Start new Threads t1.start() t2.start() # Tell the main thread to wait for other two threads to complete its execution. t1.join() t2.join() print("Exiting Main Thread")
That’s it. Thanks for reading!
About CauseCode: We are a technology company specializing in Healthtech related Web and Mobile application development. We collaborate with passionate companies looking to change health and wellness tech for good. If you are a startup, enterprise or generally interested in digital health, we would love to hear from you! Let's connect at firstname.lastname@example.org