Python Multithreaded Programming

Before going to Python Multithreaded Programming, let’s understand what is the thread? A thread is an entity within a process that can be scheduled for execution. It’s the smallest unit of processing that can be performed in an Operating System. A thread can be defined as a sequence of such instructions within a program that can be executed independently of other code. Running several threads is similar to running several different programs concurrently, but with the following benefits given below:

  • Multiple threads within a process share the same data space with the main thread and can therefore share information or communicate with each other more easily than if they were separate processes.
  • Threads sometimes called light-weight processes and they do not require much memory overhead; they are cheaper than processes.

A thread incorporates a starting, an execution arrangement, and a conclusion. It has an instruction pointer that keeps track of where inside its context it is as of now running.

  • It can be pre-empted (interrupted).
  • It can briefly be put on hold (too known as resting) whereas other strings are running – typically called Yielding.

Starting a New Thread:

To produce another thread, you would like to call following method accessible in thread module:

Syntax:
If there are ay arguments:

Method call enables a fast and efficient way to create new threads in both Linux and Windows.

The method call returns immediately and the child thread starts and calls a function with the passed list of args. When the function returns, the thread terminates.

Here, args is a tuple of arguments; use an empty tuple to call function without passing any arguments. kwargs is an optional dictionary of keyword arguments.

Example:
import threading
import time
# Define  function for the thread
def myThread( threadName, sleepTime):
   count = 0
   while count < 5:
      time.sleep(sleepTime)
      count += 1
      print (threadName,"count=",count, time.ctime(time.time()))
#Creating two threads as follows
try:
   threading.Thread( target=myThread, args=("Thread-1", 1, )).start()
   threading.Thread( target=myThread, args=("Thread-2", 4, )).start()
except:
   print ("Error: unable to start a thread")
while 1:
   pass

Threading Module:

The threading module included with Python 2.4 provides more powerful, high-level support for threads. The threading module exposes all the methods of the thread module and provides some additional methods as given below:

  • threading.activeCount() − Returns the number of thread objects that are active.
  • threading.currentThread() − Returns the number of thread objects in the caller’s thread control.
  • threading.enumerate() − Returns a list of all thread objects that are currently active.

In methods, the threading module has the Thread class that implements threading. The methods provided by the Thread class are given as follows:

  • run() − The run() method is the entry point for a thread.
  • start() − The start() method starts a thread by calling the run method.
  • join([time]) − The join() waits for threads to terminate.
  • isAlive() − The isAlive() method checks whether a thread is still executing.
  • getName() − The getName() method returns the name of a thread.
  • setName() − The setName() method sets the name of a thread.

Creating Thread Using Threading Module:

To implement a new thread using the threading module, there are some steps given following:

  1. Define a new subclass of the Thread class.
  2. Override the __init__(self [,args]) method to add additional arguments.
  3. Then, override the run(self [, args]) method to implement what the thread should do when started.

Once the new Thread subclass has created, It can create an instance of it and then start a new thread by invoking the start(), which in turn calls the run() method.

Example:
#!/usr/bin/python

import threading
import time

exitFlag = 0

class ThreadingExample (threading.Thread):
   def __init__(self, threadID, name, counter):
      threading.Thread.__init__(self)
      self.threadID = threadID
      self.name = name
      self.counter = counter
   def run(self):
      print ("Starting ", self.name)
      myThread(self.name, 5, self.counter)
      print ("Exiting ", self.name)

def myThread(threadName, counter, delay):
   while counter:
      if exitFlag:
         threadName.exit()
      time.sleep(delay)
      print (threadName, time.ctime(time.time()))
      counter -= 1

# Creating new threads
thread1 = ThreadingExample(1, "Thread-1", 1)
thread2 = ThreadingExample(2, "Thread-2", 4)

# Starting new Threads
thread1.start()
thread2.start()

print ("Exiting Main Thread")

Synchronizing Threads:

The threading module given with Python includes a simple-to-implement locking component that allows synchronizing threads. A modern lock is made by calling the Lock() strategy, which returns the new lock. The acquire/blocking method of the new lock object is used to force threads to run synchronously. The optional blocking parameter enables to control whether the thread waits to acquire the lock.

If blocking is set to 0, the thread returns immediately with a 0 value if the lock cannot be acquired and with a 1 if the lock was acquired.

If blocking is set to 1, the thread blocks and wait for the lock to be released.

The release() method of the new lock object is used to release the lock when it is no longer required.

Example:
#!/usr/bin/python

import threading
import time

class threadingExampleClass (threading.Thread):
   def __init__(self, threadID, name, count):
      threading.Thread.__init__(self)
      self.threadID = threadID
      self.name = name
      self.counter = count
   def run(self):
      print ("Starting ", self.name)
      # Get lock to synchronize threads
      threadLock.acquire()
      myThread(self.name, self.counter, 3)
      # Free lock to release next thread
      threadLock.release()

def myThread(threadName, delay, counter):
   while counter:
      time.sleep(delay)
      print (threadName, time.ctime(time.time()))
      counter -= 1

threadLock = threading.Lock()
threadsVar = []

# Creating new threads
thread1 = threadingExampleClass(1, "Thread-1", 1)
thread2 = threadingExampleClass(2, "Thread-2", 2)

# Starting new Threads
thread1.start()
thread2.start()

# Adding threads to thread list
threadsVar.append(thread1)
threadsVar.append(thread2)

# Wait for all threads to complete
for th in threadsVar:
    th.join()
print ("Exiting Main Thread")

Multithreaded Priority Queue:

The Queue module allows to create a new queue object that can hold a specific number of items.

Methods to control the Queue are given below:

  • get() − The get() removes and returns an item from the queue.
  • qsize() − The qsize() returns the number of items that are currently in the queue.
  • put() − The put adds item to a queue.
  • full() − the full() returns True if queue is full; otherwise, False.
  • empty() − The empty( ) returns True if queue is empty; otherwise, False.
Example:
#!/usr/bin/python

import queue
import threading
import time

exitFlag = 0

class threadingExampleClass  (threading.Thread):
   def __init__(self, threadID, name, que):
      threading.Thread.__init__(self)
      self.threadID = threadID
      self.name = name
      self.q = que
   def run(self):
      print ("Starting " + self.name)
      process_data(self.name, self.q)
      print ("Exiting from ", self.name)

def process_data(threadName, que):
   while not exitFlag:
      queLock.acquire()
      if not workQueue.empty():
         data = que.get()
         queLock.release()
         print (threadName, data)
      else:
         queLock.release()
         time.sleep(1)

listThreads = ["Thread-1", "Thread-2", "Thread-3"]
listNames = ["One", "Two", "Three", "Four", "Five"]
queLock = threading.Lock()
workQueue = queue.Queue(10)
threadsVar = []
threadID = 1

# Creating new threads
for tName in listThreads:
   thread = threadingExampleClass (threadID, tName, workQueue)
   thread.start()

   threadsVar.append(thread)
   threadID += 1

# Fill the queue
queLock.acquire()
for word in listNames:
   workQueue.put(word)
queLock.release()

# Wait for the queue to empty
while not workQueue.empty():
   pass

# Notify threads it's time to exit
exitFlag = 1

# Wait for all threads to complete
for th in threadsVar:
   th.join()
print ("Exiting Main Thread")