In this article we will discuss how to create threads in python that will run a function (with or without arguments) in parallel to main thread.
Python provides a threading module to manage threads. To use that we need to import this module i.e.
import threading
Now Python’s threading module provides a Thread class to create and manage threads. Thread class provides a constructor in which we can pass a callable entity i.e. function or member function etc and arguments require by that function in args i.e.
class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
For creating threads we can create objects of this Thread class by passing function which we want to run in separate thread. Each Thread class object represents a thread and we can control that thread by calling member function of this thread object.
Let’s create a thread using Thread class by passing a function with or without arguments.
Create a Thread with a function
Suppose we have a function that print 5 lines in a loop and sleeps for 1 second after printing every line i.e.
Frequently Asked:
- Get statistics for each group using Pandas GroupBy
- Python : How to delete a directory recursively using shutil.rmtree()
- Pandas: Select rows with NaN in any column
- Write to csv file without blank line in Python
''' This function will print 5 lines in a loop and sleeps for 1 second after printing a line. ''' def threadFunc(): for i in range(5): print('Hello from new Thread ') time.sleep(1)
When called, this function will complete in around 5 seconds.
As our main function runs in main thread, we want to create a new thread that will execute threadFunc() in parallel to main thread.
For that new need to create a Thread class object and pass function name (which we want to execute in new thread) in target argument i.e.
# Create a Thread with a function without any arguments th = threading.Thread(target=threadFunc)
It will create Thread class object th that can run the function provided in target argument in parallel thread, but thread has not started yet. To start the thread we need to call the start() member function from thread object i.e.
# Start the thread th.start() # Print some messages on console for i in range(5): print('Hi from Main Thread') time.sleep(1) # Wait for thread to finish th.join()
th.start() will start a new thread, which will execute the function threadFunc() in parallel to main thread. After calling start() function on thread object, control will come back to Main thread and new thread will execute in parallel to Main thread.
So, both main() function and threadFunc() will run in parallel and print logs in parallel for around 5 seconds. Therefore output of above code is,
Hello from new Thread Hi from Main Thread Hello from new Thread Hi from Main Thread Hi from Main Thread Hello from new Thread Hello from new Thread Hi from Main Thread Hi from Main Thread Hello from new Thread
In the end main thread will wait for thread th to exit by calling join() function on the thread object. This call is blocking until thread pointed by object exits.
Why main thread called the join() ?
If we haven’t called the join() function in main thread, then main() function will not wait for thread pointed by th to finish. So, if main() function finishes it’s work first, it can exit without for other thread to finish. Therefore calling join() on thread object will reduce the bugs.
Complete example is as follows,
import threading import time ''' This function will print 5 lines in a loop and sleeps for 1 second after printing a line. ''' def threadFunc(): for i in range(5): print('Hello from new Thread ') time.sleep(1) def main(): print('**** Create a Thread with a function without any arguments ****') # Create a Thread with a function without any arguments th = threading.Thread(target=threadFunc) # Start the thread th.start() # Print some messages on console for i in range(5): print('Hi from Main Thread') time.sleep(1) # Wait for thread to finish th.join() if __name__ == '__main__': main()
Output:
**** Create a Thread with a function without any arguments **** Hello from new Thread Hi from Main Thread Hello from new Thread Hi from Main Thread Hi from Main Thread Hello from new Thread Hello from new Thread Hi from Main Thread Hi from Main Thread Hello from new Thread
Create a Thread from a function with arguments
What if we have a function that accepts few arguments i.e.
''' A Dummy function that accepts 2 arguments i.e. Filename and encryption type and sleeps for 5 seconds in a loop while printing few lines. This is to simulate a heavey function that takes 10 seconds to complete ''' def loadContents(fileName, encryptionType): print('Started loading contents from file : ', fileName) print('Encryption Type : ', encryptionType) for i in range(5): print('Loading ... ') time.sleep(1) print('Finished loading contents from file : ', fileName)
This function is a simulation of a heavy function that accepts two arguments i.e. filename and encryption type, then does some stuff that takes around 5 seconds.
Now to create a thread object that runs this function in parallel thread, we need to pass the function arguments as tuple in args argument of the Thread class constructor i.e.
# Create a thread from a function with arguments th = threading.Thread(target=loadContents, args=('users.csv','ABC' )) # Start the thread th.start() # print some lines in main thread for i in range(5): print('Hi from Main Function') time.sleep(1) # Wait for thread to finish th.join()
It will create a thread object that can run the passed function in new thread in parallel to main thread. Both the main() function and loadContents() will run in parallel and both will print logs in parallel. Therefore output of the above code is ,
Started loading contents from file : users.csv Hi from Main Function Encryption Type : ABC Loading ... Hi from Main Function Loading ... Hi from Main Function Loading ... Hi from Main Function Loading ... Hi from Main Function Loading ... Finished loading contents from file : users.csv
Another Important Point is if we have a function that accepts a single argument then we need to pass the tuple in args argument with extra comma like this,
th = threading.Thread(target=someFunction, args=('sample' , ))
Complete example is as follows,
import threading import time ''' A Dummy function that accepts 2 arguments i.e. Filename and encryption type and sleeps for 5 seconds in a loop while printing few lines. This is to simulate a heavey function that takes 10 seconds to complete ''' def loadContents(fileName, encryptionType): print('Started loading contents from file : ', fileName) print('Encryption Type : ', encryptionType) for i in range(5): print('Loading ... ') time.sleep(1) print('Finished loading contents from file : ', fileName) def main(): print('**** Create a Thread with a function with arguments ****') # Create a thread from a function with arguments th = threading.Thread(target=loadContents, args=('users.csv','ABC' )) # Start the thread th.start() # print some lines in main thread for i in range(5): print('Hi from Main Function') time.sleep(1) # Wait for thread to finish th.join() if __name__ == '__main__': main()
Output:
**** Create a Thread with a function with arguments **** Started loading contents from file : Hi from Main Function users.csv Encryption Type : ABC Loading ... Hi from Main Function Loading ... Hi from Main Function Loading ... Hi from Main Function Loading ... Hi from Main Function Loading ... Finished loading contents from file : users.csv
Important Point about Outputs:
In above examples both main thread and our new thread are running in parallel and print messages on the console in parallel. Therefore order of outputs may vary from above outputs because console is a shared resource used by 2 threads in parallel. In future articles we will discuss how to synchronize a single resource between threads.