[EN] Python multi-threaded programming

This article discusses Python multi-threaded programming. Compiled from the website tutorialspoint.com (Make this article a memo). Running multiple threads is like running several different programs at the same time, but it’s useful:

  • Each thread can share the memory with the main thread and can communicate with each other.
  • Threads are smaller processes because they consume less memory than process calls.

Thread

Thread is a processor. When the thread starts The work will be in the order in which it is finished. In thread execution, there is a pointer variable of the command being processed, where

  • Threads can be pre-empted or interrupted.
  • Thread can be stopped while another thread is running (called yielding).

Thread type

Threads are divided into two types:

  • Kernel thread is a part of OS
  • User thread

Threads supported by Python3

Python 3 supports both new and old threads with two classes:

  • _thread for thread compatibility from older versions of Python.
  • threading is designed for use with Python 3 and is a low-level thread.

In Python, the thread is a class that covers threading.

Thread creation can be done using the following build pattern:

_thread.start_new_thread( function, args [ , kwargs ] )

An example of creating two threads with the names thread-1 and thread-2 caused by the execution of the function print_time, but with the difference that thread-1 passes the value of 2  for the delay and thread- 2 passed a value of 4. So both threads are running with different delay values.

# -*- coding: UTF-8 -*-
import _thread
import time

def print_time( thread_name, delay ):
	count = 0
	while count < 5:
		time.sleep( delay )
		count += 1
		print(thread_name + str(time.ctime(time.time())))


def main():
	try:
		_thread.start_new_thread( print_time, ("thread-1", 2))
		_thread.start_new_thread( print_time, ("thread-2", 4))
	except:
		print("Error: unable to start thread")
	while True:
		pass

if __name__=="__main__":
	main()

The sample result when running on Raspberry Pi 3 is as shown in Figure 1.

(Figure. 1 Result of t0.py)

Thread method

Methods supported by threading are:

  • threading.activeCount() returns the total number of threads created.
  • threading.currentThread() returns the number of threads created by the current thread.
  • threading.enumerate() returns a list of active threads.

Thread methods are:

  • run() is the starting point of the thread.
  • start() is a startup method called by run() .
  • join([time]) wait until the thread terminates.
  • isAlive() check if the thread is running.
  • getName() returns the name of the thread.
  • setName() give the thread a name.

Creating a thread from the following example’s threading class inherits three parts:

  • Create a subclass named myThread that inherits from the Thread class.
  • overwrite method init( self [, args] )
  • overwrite method run( self [, args] ) to write the section to do when the thread starts.
# -*- coding: UTF-8 -*-
import threading
import time
class myThread(threading.Thread):
	def __init__( self, thread_id, name, counter ):
		threading.Thread.__init__( self )
		self.thread_id = thread_id
		self.name = name
		self.counter = counter
	def run( self ):
		print("Starting "+self.name)
		print_time( self.name, self.counter, 5 )
		print("Exit "+self.name)
running = 0
def print_time( thread_name, delay, counter ):
	while counter:
		if running:
			thread_name.exit()
		time.sleep(delay)
		print(thread_name +":"+str(time.ctime(time.time())))
		counter -= 1
def main():
	thread1 = myThread(1, "thread-1", 1)
	thread2 = myThread(2, "thread-2", 2)
	thread1.start()
	thread2.start()
	thread1.join()
	thread2.join()
	print("Exiting Main Thread")

if __name__=="__main__":
	main()

An example of working results on Raspberry Pi 3 is shown in Figure 2.

(Figure. 2 Result of t1.py)

Synchronizing Threads

Python 3 has a Lock() method that makes it easier to synchronize threads. A blocking parameter can be passed to a locked thread to control whether to wait or not wait from the lock.

  • If blocking is 0, the thread returns 0 when blocking fails, and 1 if locking succeeds.
  • If blocking is 1, the thread is blocked and waits until the lock is cancelled.

and the release() method is used to unlock the thread. As an example of the following program.

# -*- coding: UTF-8 -*-
import threading
import time

thread_lock = threading.Lock()
threads = []

def print_time( thread_name, delay, counter ):
	while counter:
		time.sleep(delay)
		print(thread_name +":"+str(time.ctime(time.time())))
		counter -= 1

class myThread(threading.Thread):
	def __init__( self, thread_id, name, counter ):
		threading.Thread.__init__( self )
		self.thread_id = thread_id
		self.name = name
		self.counter = counter
	def run( self ):
		print("Starting "+self.name)
		thread_lock.acquire()
		print_time( self.name, self.counter, 5 )
		thread_lock.release()
def main():
	# Create new threads
	thread1 = myThread(1, "Thread-1", 1)
	thread2 = myThread(2, "Thread-2", 2)
	# Start new Threads
	thread1.start()
	thread2.start()
	# Add threads to thread list
	threads.append(thread1)
	threads.append(thread2)
	# Wait for all threads to complete
	for t in threads:
		t.join()
	print("Exiting Main Thread")

if __name__=="__main__":
	main()

An example of the result of working on Raspberry Pi 3 is shown in Figure 3.

(Figure. 3 Result of t2.py)

Multi-threaded Priority Queue

Multi-threaded priority queues is the application of a queue-threaded data structure that enables an unlimited number of threads to be handled (Depending on the amount of memory available), the method for controlling the queue is as follows.

  • get() removes an item from the queue
  • put() adds item to the queue
  • qsize() returns the number of items in the queue.
  • empty() checks the status of the queue if the queue is empty or not.
  • full() checks whether the queue status is full or not.

An example program of using a multi-threaded priority queue is as follows.

# -*- coding: UTF-8 -*-
import threading
import time
import queue

running = 1
thread_list = ["Thread-1", "Thread-2", "Thread-3"]
name_list = ["One", "Two", "Three", "Four", "Five"]
queue_lock = threading.Lock()
work_queue = queue.Queue(10)
threads = []
thread_ID = 1

def process_data(thread_name, q):
	while running:
		queue_lock.acquire()
		if not work_queue.empty():
			data = q.get()
			queue_lock.release()
			print( thread_name+" processing "+data)
		else:
			queue_lock.release()
			time.sleep(1)
class myThread(threading.Thread):
	def __init__( self, thread_id, name, q ):
		threading.Thread.__init__( self )
		self.thread_id = thread_id
		self.name = name
		self.q = q
	def run( self ):
		print("Starting "+self.name)
		process_data(self.name, self.q)
		print("Exit "+self.name)

for t_name in thread_list:
	thread = myThread(thread_ID, t_name, work_queue)
	thread.start()
	threads.append(thread)
	thread_ID += 1
queue_lock.acquire()
for word in name_list:
	work_queue.put(word)
queue_lock.release()
while not work_queue.empty():
	pass
running = False
for t in threads:
	t.join()
print ("Exiting Main Thread")
thread_lock = threading.Lock()
threads = []

An example of the result of working from the above code with Raspberry Pi 3 is shown in Figure 4.

(Figure. 4 Result of t3.py)

Conclusion

All of them are of the different types of threads supported by Python version 3 and some basic usage examples for further study. It is found that nowadays there are many additional resources and languages. Therefore, programming students must adapt to the rapid changes and study from various sources regularly. And we will bring the articles compiled during the reading of these documents to be updated to read again. Finally, have fun with programming.

If you want to talk with us, feel free to leave comments below!!

Reference

  1. Tutorialspoint.com : Python-Multithreaded programming

From Python-Multithread programming By Jarut busarathid and Danai Jedsadathitikul
Updated 2021-11-23