Python Thread – Part 1

Python Thread

A Python thread is like a process, and may even be a process, depending on the Python thread system. In fact, Python threads are sometimes called “lightweight” processes, because threads occupy much less memory, and take less time to create than do processes.

Threads allow applications to perform multiple tasks at once. Multi-threading is important in many applications.

In python, threading module is used to create the threads. In order to create threads, threading modules can be used in two ways. First, by inheriting the class and second by the use of thread function.

Python Thread creation using class :

Let us understand by using code.

import threading

class mythread(threading.Thread):

    def __init__(self,i):

       threading.Thread.__init__(self)

       self.h =i

    def run(self):

       print "Value send ", self.h

thread1 = mythread(1)
thread1.start()
  • New class mythread inherits the Python threading.Thread class
  • __init__(self [,args]) Override the constructor
  • run():  This is the section where you can put your logic part
  • start(): The start() method starts a Python thread
  • The mythread class overrides the constructor, so base class constructor (Thread.__init__()) must be invoked.

Python Thread creation using function :

import threading

def fun1(a,b):

    c = a+b

    print (c)

thread1 = threading.Thread(target=fun1, args=(12,10))

thread1.start()

The above code is very simple, just create a function and get it run by a thread. The syntax

threading.Thread(target=fun1, args=(12,10)) creates a thread, the target = fun1

specifies the function to be run and args indicates the tuple which contains the argument to be passed to the function.

Python Training

Important threading methods

Let us see some important methods, defined in the threading modules.

threading.activeCount(): Returns the number of total Python thread that are active.

threading.activeCount(): Returns the number of total Python thread that are active.

Let us understand by the example.

import threading

import time

def fun1(a,b):

   time.sleep(1)

   c = a+b

   print (c)


thread1 = threading.Thread(target=fun1, args=(12,10))

thread1.start()


thread2 = threading.Thread(target=fun1, args=(10,17))

thread2.start()

print ("Total number of threads", threading.activeCount())


print ("List of threads : ", threading.enumerate())

Let us see the output.

thread1
Figure 1 : All threads and thread list

Now comment the line time.sleep(1) and then run the code again.
thread2
Figure 2:  Without sleep time

You can see the difference. The statement threading.activeCount() and threading.enumerate() have been run the main thread or main process. So, the main thread is responsible to run the entire program. In figure 1 when main thread executed the threading.activeCount(), that time thread1 and thread2 were active.

When we commented the time.sleep(1) syntax, and executed the code again then thread1 and thread3 might not be active.

The join method

Before discussing the significance of join method, let us see the following program.

import threading

import time


list1 = []

def fun1(a):

   time.sleep(1) # complex calculation takes 1 seconds

   list1.append(a)


thread1 = threading.Thread(target=fun1, args=(1,))

thread1.start()


thread2 = threading.Thread(target=fun1, args=(6,))

thread2.start()


print ("List1 is : ", list1)

In the above program, the two threads have been created with arguments. In target function fun1, a global list list1 is appended with an argument. Let us see the result.
thread3
Figure 3: Showing Empty list

The above figure is showing the empty list, but the list is expected to be filled with values 1 and 6. The statement print (“List1 is : “, list1) is executed by the main thread and main thread printed the list before getting it filled.

So main thread must be paused until all threads complete their jobs. To achieve the same, we shall use the join method.

Let see the modified code.

import threading

import time

list1 = []

def fun1(a):

   time.sleep(1) # complex calculation takes 1 seconds

   list1.append(a)

thread1 = threading.Thread(target=fun1, args=(1,))

thread1.start()


thread2 = threading.Thread(target=fun1, args=(6,))

thread2.start()


thread1.join()

thread2.join()


print ("List1 is : ", list1)

Let us see the output.
thread4
Figure 4: use of join method

In the above code the syntax thread1.join() block the main thread until thread1 finishes its task. In order to achieve parallelism, join method must be called after creation of all threads.

Let us see more use case of join methods.

import threading

import time

import datetime


t1 = datetime.datetime.now()


list1 = []

def fun1(a):

   time.sleep(1) # complex calculation takes 1 seconds

   list1.append(a)

list_thread = []

for each in range(10):

   thread1 = threading.Thread(target=fun1, args=(each,))

   list_thread.append(thread1)

   thread1.start()

for th in list_thread:

   th.join()


print ("List1 is : ", list1)

t2 = datetime.datetime.now()

print ("Time taken", t2-t1)

In the above code, we have used for loop to create 10 threads. Each thread is getting appended in the list list_thread. After creation of all threads, we used the join method. Let us see the output.

thread5
Figure 5: Out of join with loop

Time taken to execute is approx. 1 second. Since every thread take 1 second and we used thread-based parallelism that’s why total time is near to 1 second. If you use the join method after the creation of each thread program would take more than 10 seconds.

For more clarification see the following code.

import threading

import time

import datetime

t1 = datetime.datetime.now()

list1 = []

def fun1(a):

   time.sleep(1) # complex calculation takes 1 seconds

   list1.append(a)

for each in range(10):

   thread1 = threading.Thread(target=fun1, args=(each,))

   thread1.start()

   thread1.join()

print ("List1 is : ", list1)

t2 = datetime.datetime.now()

print ("Time taken", t2-t1)

Output:
thread6
Figure 6: Thread with join method

From the above output, it is clear that, time taken is 10 seconds, it means no parallelism has been achieved because Join() method of first thread has been called before the creation of the second thread.

Method join with time

Let us see the following piece of code.

import threading

import time

def fun1(a):

   time.sleep(3) # complex calculation takes 3 seconds

thread1 = threading.Thread(target=fun1, args=(1,))

thread1.start()

thread1.join()

print (thread1.isAlive())

The couples of things are new here. The isAlive() method returns the True or False. If the thread is currently active then the method isAlive() returns True otherwise False.

Output:
thread7

The above output shows thread is not active.

Let make a small change, make thread1.join() to thread1.join(2). It means, block the main thread only for 2 seconds.

Let us see the output:
thread8

In the above output, show thread was still active because join(2) kept blocking the main thread for 2 seconds but the thread took 3 seconds to complete its task.

threading.Timer()

This method is used set the time. Let us understand the syntax.

threading.Timer(interval, function, args=[], kwargs={})

The above syntax meaning is, after specified interval in seconds interpreter will execute the function with args and kwargs.

Python Treading

Leave a reply:

Your email address will not be published.

Site Footer