[Python 3 learning notes - getting started to magic series] want to make your program execute faster? Multithreading

Multithreading is similar to executing multiple different programs at the same time. Multithreading has the following advantages:

If you are interested in learning Python together, click the following icon to add a group:

  • Using threads, you can put tasks in programs that occupy a long time in the background.
  • The user interface can be more attractive. For example, if the user clicks a button to trigger the processing of some events, a progress bar can pop up to display the processing progress.
  • The program may run faster.
  • In the implementation of some waiting tasks, such as user input, file reading and writing, network sending and receiving data, threads are more useful. In this case, we can release some precious resources, such as memory occupation and so on.

Each independent thread has an entry for program operation, sequential execution sequence and program exit. However, threads cannot be executed independently. They must be stored in the application, and the application provides multiple thread execution control.

Each thread has its own set of CPU registers, called the thread context, which reflects the state of the CPU register in which the thread last ran.

Instruction pointer and stack pointer registers are the two most important registers in the thread context. Threads always run in the process context. These addresses are used to mark the memory in the process address space of the thread.

  • Threads can be preempted (interrupted).
  • Threads can be temporarily suspended (also known as sleep) while other threads are running - this is the concession of threads.

Threads can be divided into:

  • **Kernel thread: * * created and revoked by the operating system kernel.
  • **User thread: * * thread implemented in user program without kernel support.

Two modules commonly used in Python 3 threads are:

  • _thread
  • Threading (recommended)

Thread module is obsolete. Users can use threading module instead. Therefore, the "thread" module can no longer be used in Python 3. For compatibility, python 3 renamed the thread to "_thread".

Start learning Python threads

There are two ways to use threads in Python: functions or wrapping thread objects with classes.

Functional: calling_ Start in thread module_ new_ Thread() function to generate a new thread. The syntax is as follows:

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

Parameter Description:

  • Function - thread function.
  • args - the parameter passed to the thread function. It must be of type tuple.
  • kwargs - optional parameter.

example:

#!/usr/bin/python3

import _thread
import time

# Define a function for the thread
def print_time( threadName, delay):
   count = 0
   while count < 5:
      time.sleep(delay)
      count += 1
      print ("%s: %s" % ( threadName, time.ctime(time.time()) ))

# Create two threads
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

Execute the above procedure and the output results are as follows:

Thread-1: Wed Apr  6 11:36:31 2016
Thread-1: Wed Apr  6 11:36:33 2016
Thread-2: Wed Apr  6 11:36:33 2016
Thread-1: Wed Apr  6 11:36:35 2016
Thread-1: Wed Apr  6 11:36:37 2016
Thread-2: Wed Apr  6 11:36:37 2016
Thread-1: Wed Apr  6 11:36:39 2016
Thread-2: Wed Apr  6 11:36:41 2016
Thread-2: Wed Apr  6 11:36:45 2016
Thread-2: Wed Apr  6 11:36:49 2016

After executing the above procedure, you can press ctrl-c to exit.

Thread module

Python 3 uses two standard libraries_ thread and threading provide support for threads.

_ thread provides low-level, original threads and a simple lock. Compared with threading module, its function is still relatively limited.

The threading module contains_ In addition to all methods in the thread module, other methods are also provided:

  • threading.currentThread(): returns the current thread variable.
  • threading.enumerate(): returns a list of running threads. Running refers to threads after starting and before ending, excluding threads before starting and after termination.
  • threading.activeCount(): returns the number of running threads, which is the same as len(threading.enumerate()).

In addition to using methods, the thread module also provides thread class to process threads. Thread class provides the following methods:

  • run(): the method used to represent thread activity.

  • start():

    Start thread activity.

  • join([time]): wait until the thread aborts. This blocks the calling thread until the thread's join() method is called to abort - exit normally or throw an unhandled exception - or an optional timeout occurs.

  • isAlive(): Returns whether the thread is active.

  • getName(): returns the thread name.

  • setName(): sets the thread name.

Creating threads using the threading module

We can create a new subclass by directly inheriting from threading.Thread, and instantiate and call the start() method to start the new thread, that is, it calls the run() method of the thread:

#!/usr/bin/python3

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 ("Start thread:" + self.name)
        print_time(self.name, self.counter, 5)
        print ("Exit thread:" + self.name)

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

# Create a new thread
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# Open new thread
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("Exit main thread")

The results of the above procedures are as follows:

Start thread: Thread-1
 Start thread: Thread-2
Thread-1: Wed Apr  6 11:46:46 2016
Thread-1: Wed Apr  6 11:46:47 2016
Thread-2: Wed Apr  6 11:46:47 2016
Thread-1: Wed Apr  6 11:46:48 2016
Thread-1: Wed Apr  6 11:46:49 2016
Thread-2: Wed Apr  6 11:46:49 2016
Thread-1: Wed Apr  6 11:46:50 2016
 Exit thread: Thread-1
Thread-2: Wed Apr  6 11:46:51 2016
Thread-2: Wed Apr  6 11:46:53 2016
Thread-2: Wed Apr  6 11:46:55 2016
 Exit thread: Thread-2
 Exit main thread

Thread synchronization

If multiple threads modify a data together, unexpected results may occur. In order to ensure the correctness of the data, multiple threads need to be synchronized.

Simple thread synchronization can be realized by using Lock and Rlock of Thread objects. Both objects have acquire and release methods. For data that needs to be operated by only one thread at a time, its operation can be placed between acquire and release methods. As follows:

The advantage of multithreading is that it can run multiple tasks at the same time (at least it feels like this). However, when threads need to share data, there may be a problem of data synchronization.

Consider this situation: all elements in a list are 0, the thread "set" changes all elements to 1 from back to front, and the thread "print" is responsible for reading and printing the list from front to back.

Then, when the thread "set" starts to change, the thread "print" will print the list, and the output will be half 0 and half 1, which is the asynchrony of data. In order to avoid this situation, the concept of lock is introduced.

The lock has two states - locked and unlocked. Whenever a thread, such as "set", wants to access shared data, it must first obtain a lock; If another thread, such as "print", has been locked, the thread "set" is suspended, that is, synchronization blocking; Wait until the thread "print" is accessed and the lock is released, and then let the thread "set" continue.

After such processing, when printing the list, either all 0 or all 1 will be output, and the embarrassing scene of half 0 and half 1 will not appear again.

example:

#!/usr/bin/python3

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 ("Open thread: " + self.name)
        # Get lock for thread synchronization
        threadLock.acquire()
        print_time(self.name, self.counter, 3)
        # Release the lock and start the next thread
        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 = []

# Create a new thread
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# Open new thread
thread1.start()
thread2.start()

# Add thread to thread list
threads.append(thread1)
threads.append(thread2)

# Wait for all threads to complete
for t in threads:
    t.join()
print ("Exit main thread")

Execute the above procedure, and the output result is:

Open thread: Thread-1
 Open thread: Thread-2
Thread-1: Wed Apr  6 11:52:57 2016
Thread-1: Wed Apr  6 11:52:58 2016
Thread-1: Wed Apr  6 11:52:59 2016
Thread-2: Wed Apr  6 11:53:01 2016
Thread-2: Wed Apr  6 11:53:03 2016
Thread-2: Wed Apr  6 11:53:05 2016
 Exit main thread

Thread priority Queue

Python's Queue module provides synchronized and thread safe Queue classes, including FIFO (first in first out) Queue, LIFO (last in first out) Queue, LifoQueue, and priority Queue PriorityQueue.

These queues implement lock primitives and can be used directly in multithreading. Queues can be used to achieve synchronization between threads.

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.full corresponds to the maxsize size
  • Queue.get([block[, timeout]]) gets the queue and timeout the waiting time
  • Queue.get_nowait() is equivalent to Queue.get(False)
  • Queue.put(item) writes to the queue and timeout the waiting time
  • Queue.put_nowait(item) is equivalent to Queue.put(item, False)
  • Queue.task_done() after completing a task, queue. Task_ The done () function sends a signal to the queue where the task has been completed
  • Queue.join() actually means waiting until the queue is empty before doing anything else

example:

#!/usr/bin/python3

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 ("Open thread:" + self.name)
        process_data(self.name, self.q)
        print ("Exit thread:" + 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 a new thread
for tName in threadList:
    thread = myThread(threadID, tName, workQueue)
    thread.start()
    threads.append(thread)
    threadID += 1

# Fill queue
queueLock.acquire()
for word in nameList:
    workQueue.put(word)
queueLock.release()

# Waiting for queue to clear
while not workQueue.empty():
    pass

# Notify the thread when to exit
exitFlag = 1

# Wait for all threads to complete
for t in threads:
    t.join()
print ("Exit main thread")

Execution results of the above procedures:

Open thread: Thread-1
 Open thread: Thread-2
 Open thread: Thread-3
Thread-3 processing One
Thread-1 processing Two
Thread-2 processing Three
Thread-3 processing Four
Thread-1 processing Five
 Exit thread: Thread-3
 Exit thread: Thread-2
 Exit thread: Thread-1
 Exit main thread

If you are interested in learning Python together, click the following icon to add a group:

Tags: Python

Posted on Sun, 24 Oct 2021 14:38:27 -0400 by raje