Python2.x Python Multithreading
Multithreading is similar to running multiple different programs simultaneously. The advantages of multithreading include:
- Using threads can move time-consuming tasks in a program to the background for processing.
- The user interface can be more attractive. For example, when a user clicks a button to trigger some event processing, a progress bar can pop up to show the processing progress.
- The speed of the program may increase.
- Threads are useful in certain waiting tasks such as user input, file reading and writing, and network data transmission. In these cases, we can release some precious resources such as memory usage, etc.
There are differences between threads and processes during execution. Each independent process has an entry point for program execution, a sequence of execution, and an exit point for the program. However, threads cannot execute independently and must exist within an application, with the application providing multiple threads for execution control.
Each thread has its own set of CPU registers, known as the thread context, which reflects the state of the CPU registers when the thread last ran the thread.
The instruction pointer and stack pointer registers are two of the most important registers in the thread context. Threads always run within the context of a process, and these addresses are used to indicate memory in the address space of the process that owns the thread.
- Threads can be preempted (interrupted).
- Threads can temporarily be put on hold (also called yielding) when other threads are runningβthis is thread yielding.
There are two ways to use threads in Python: through functions or by wrapping thread objects in classes.
Functional: Call the start_new_thread() function in the thread module to create a new thread. Syntax:
thread.start_new_thread ( function, args[, kwargs] )
Parameter description:
function- The thread function.args- The arguments passed to the thread function, it must be a tuple type.kwargs- Optional parameters.
Example (Python 2.0+)
import thread
import time
def print_time(threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print "%s: %s" % (threadName, time.ctime(time.time()))
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 1:
pass
The output of the above program is as follows:
Thread-1: Thu Jan 22 15:42:17 2009
Thread-1: Thu Jan 22 15:42:19 2009
Thread-2: Thu Jan 22 15:42:19 2009
Thread-1: Thu Jan 22 15:42:21 2009
Thread-2: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:25 2009
Thread-2: Thu Jan 22 15:42:27 2009
Thread-2: Thu Jan 22 15:42:31 2009
Thread-2: Thu Jan 22 15:42:35 2009
The end of a thread generally relies on the natural end of the thread function; it can also call thread.exit() in the thread function, which throws a SystemExit exception to achieve the purpose of exiting the thread.
Thread Module
Python supports threads through two standard libraries, thread and threading. The thread module provides low-level, primitive threads and a simple lock.
Other methods provided by the threading module:
threading.currentThread(): Returns the current thread variable.threading.enumerate(): Returns a list containing running threads. Running means after the thread starts and before it ends, excluding before starting and after termination.threading.activeCount(): Returns the number of running threads, with the same result aslen(threading.enumerate()).
In addition to using methods, the thread module also provides a Thread class to handle threads. The Thread class provides the following methods:
run(): Method representing the thread activity.start(): Starts thread activity.join(): Waits for the thread to terminate. This blocks the calling thread until the thread'sjoin()method is terminatedβnormally exits or raises an unhandled exceptionβor the optional timeout occurs.isAlive(): Returns whether the thread is alive.getName(): Returns the thread name.setName(): Sets the thread name.
Creating Threads Using the Threading Module
To create threads using the Threading module, directly inherit from threading.Thread, then rewrite the __init__ method and the run method:
Example (Python 2.0+)
import threading
import time
exitFlag = 0
class myThread(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
print_time(self.name, self.counter, 5)
print "Exiting " + self.name
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
(threading.Thread).exit()
time.sleep(delay)
print "%s: %s" % (threadName, time.ctime(time.time()))
counter -= 1
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
thread1.start()
thread2.start()
print "Exiting Main Thread"
The output of the above program is as follows:
Starting Thread-1
Starting Thread-2
Exiting Main Thread
Thread-1: Thu Mar 21 09:10:03 2013
Thread-1: Thu Mar 21 09:10:04 2013
Thread-2: Thu Mar 21 09:10:04 2013
Thread-1: Thu Mar 21 09:10:05 2013
Thread-1: Thu Mar 21 09:10:06 2013
Thread-2: Thu Mar 21 09:10:06 2013
Thread-1: Thu Mar 21 09:10:07 2013
Exiting Thread-1
Thread-2: Thu Mar 21 09:10:08 2013
Thread-2: Thu Mar 21 09:10:10 2013
Thread-2: Thu Mar 21 09:10:12 2013
Exiting Thread-2
Thread Synchronization
If multiple threads modify the same data, unexpected results may occur. To ensure data correctness, synchronization of multiple threads is required.
Simple thread synchronization can be achieved using the Lock and Rlock objects of the Thread object. Both of these objects have acquire and release methods. For data that needs to be operated on by only one thread at a time, place its operations between the acquire and release methods. As follows:
The advantage of multithreading lies in the ability to run multiple tasks simultaneously (at least it feels like that). However, when threads need to share data, there may be issues of data inconsistency.
Consider this scenario: all elements in a list are 0, thread "set" changes all elements to 1 from back to front, while thread "print" is responsible for reading the list from front to back and printing it.
So, when thread "set" starts changing, thread "print" might come to print the list, resulting in half 0s and half 1s being output, which is data inconsistency. To avoid this situation, the concept of locks is introduced.
A lock has two statesβlocked and unlocked. Whenever a thread, for example, "set," wants to access shared data, it must first acquire the lock; if another thread, for example, "print," has already acquired the lock, then thread "set" is paused, i.e., synchronously blocked; until thread "print" finishes accessing and releases the lock, thread "set" is allowed to continue.
After such processing, when printing the list, it will either output all 0s or all 1s, and it will no longer result in the awkward situation of half 0s and half 1s.
Example (Python 2.0+)
import threading
import time
class myThread(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
threadLock.acquire()
print_time(self.name, self.counter, 3)
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print("%s: %s") % (threadName, time.ctime(time.time()))
counter -= 1
threadLock = threading.Lock()
threads = []
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
thread1.start()
thread2.start()
threads.append(thread1)
threads.append(thread2)
for t in threads:
t.join()
print("Exiting Main Thread")
Thread Priority Queue (Queue)
The Queue module in Python provides synchronized, thread-safe queue classes, including FIFO (First In First Out) queues Queue, LIFO (Last In First Out) queues LifoQueue, and priority queues PriorityQueue. These queues implement lock primitives and can be used directly in multithreading. Queues can be used to implement thread synchronization.
Common methods in the Queue module:
Queue.qsize()Returns the size of the queue.Queue.empty()Returns True if the queue is empty, otherwise False.Queue.full()Returns True if the queue is full, otherwise False.Queue.fullCorresponds to the size ofmaxsize.Queue.get([block[, timeout]])Gets the queue, timeout is the waiting time.Queue.get_nowait()Equivalent toQueue.get(False).Queue.put(item, block=True, timeout=None)Writes to the queue, timeout is the waiting time.Queue.put_nowait(item)Equivalent toQueue.put(item, False).Queue.task_done()After completing a task, theQueue.task_done()function sends a signal to the queue that the task has been completed.Queue.join()Actually means waiting for the queue to be empty before performing other operations.
Example (Python 2.0+)
import Queue
import threading
import time
exitFlag = 0
class myThread(threading.Thread):
def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print("Starting ") + self.name
process_data(self.name, self.q)
print("Exiting ") + self.name
def process_data(threadName, q):
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print("%s processing %s") % (threadName, data)
else:
queueLock.release()
time.sleep(1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = Queue.Queue(10)
threads = []
threadID = 1
# Create new threads
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
# Fill the queue
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
# Wait for queue to empty
while not workQueue.empty():
pass
# Notify threads it's time to exit
exitFlag = 1
# Wait for all threads to complete
for t in threads:
t.join()
print("Exiting Main Thread")
YouTip