Thread on

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

  • Using threads allows you to put tasks in the background that occupy a long time in a program.
  • The user interface can be more attractive, so that if a user clicks a button to trigger the processing of some event, a progress bar pops up to show the progress of the processing.
  • Programs may run faster.
  • Threads are useful for waiting tasks such as user input, file reading and writing, and network data sending and receiving. In this case we can free up some precious resources such as memory footprint and so on.

Threads are different from processes in their execution. Each independent thread has an entry point for program execution, a sequential execution sequence, and an exit point for the program. However, threads cannot execute independently and must depend on the application, which provides multiple thread execution control.

Each thread has its own set of CPU registers, called the thread’s context, which reflects the state of the CPU registers from which the thread last ran.

The instruction pointer and stack pointer registers are the two most important registers in the context of a thread. A thread always runs in the context of a process. These addresses are used to identify memory in the address space of the process that owns the thread. Threads can be preempted (interrupted).

A thread can be put on hold (also known as sleep) while another thread is running — this is the thread’s fallback.

Threads can be divided into:

  • Kernel threads: Created and destroyed by the operating system kernel.
  • User threads: Threads implemented in user programs without kernel support.

Two modules commonly used in threads are:

  • _thread
  • Threading (recommended)

The Thread module has been discarded. Users can use the threading module instead. Therefore, the “thread” module is no longer available in Python3. Python3 renames threads to “_thread” for compatibility.

Start learning about Python threads

Threads are used in Python in two ways: functions or classes that wrap thread objects.

Function: Call start_new_thread() in the _thread module to generate a new thread. The syntax is as follows:

_thread.start_new_thread ( function, args[, kwargs] )
Copy the code

Parameter Description:

  • Function-thread functions.
  • Args – The argument passed to the thread function. It must be of type tuple.
  • Kwargs – This parameter is optional.

Example:

#! /usr/bin/python3 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.ctime()))))) _thread.start_new_thread( print_time, ("Thread-1", 2, ) ) _thread.start_new_thread( print_time, ("Thread-2", 4, ) except: print ("Error: cannot start thread ") while 1: passCopy the code

The output of the above program is as follows:

The threading module

Python3 provides support for threads through two standard libraries, _thread and threading.

  • _thread provides a low-level, primitive thread, and a simple lock, which is relatively limited compared to the threading module.
  • In addition to all the methods in the _thread module, the threading module provides other methods:
  • Threading.currentthread (): returns the currentThread variable.
  • Threading.enumerate (): Returns a list of running threads. Running refers to the thread after starting and before ending, excluding the thread before starting and after ending.
  • Threading.activecount (): Returns the number of threads running as len(threading.enumerate()).

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

  • Run (): A method used to indicate thread activity.
  • Start (): starts thread activity.
  • Join ([time]): waits until the thread terminates. This blocks the calling thread until the thread’s join() method is aborted by calling it – either a normal exit or an unhandled exception thrown – or an optional timeout occurs.
  • IsAlive (): Returns whether the thread is active.
  • GetName (): Returns the thread name.
  • SetName (): Sets the thread name.

Create threads using the threading module

We can create a new subclass directly from threading.thread and instantiate it by calling the start() method, which calls the Thread’s run() method:

#! /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_time (self.name, self.counter, 5) print (" " + 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) 1) thread2 = myThread(2, "Thread-2", Thread1.start () thread2.start() thread1.join() thread2.join() print ()Copy the code

The execution results of the above procedures are as follows:

Thread synchronization

If multiple threads work together to modify data, unexpected results may occur. To ensure data correctness, multiple threads need to be synchronized.

Simple Thread synchronization can be achieved using Lock and Rlock of Thread objects, both of which have acquire and release methods. For data that requires only one Thread at a time, Its operations can be placed between acquire and Release methods. As follows:

The advantage of multithreading is that you can run multiple tasks simultaneously (at least it feels that way). However, when threads need to share data, there can be data asynchronism.

Consider the case where all elements in a list are 0, the thread “set” changes all elements to 1 from back to front, and the thread “print” reads the list from front to back and prints.

So, maybe when the thread “set” starts changing, the thread “print” prints the list, and the output is half 0 and half 1, and that’s out of sync. To avoid this, the concept of locking was introduced.

Locks have two states — locked and unlocked. Whenever a thread such as “set” wants to access shared data, it must first acquire a lock; If another thread, such as “print”, has been locked, then the thread “set” is suspended, that is, synchronous blocking; Wait until the thread “print” is finished and the lock is released, then let the thread “set” continue.

After this processing, the list can be printed with either all zeros or all ones, without the awkward situation of half zeros and half ones. Examples are as follows:

#! /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: 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, Thread.ctime (thread.ctime ()))) counter -= 1 threadLock = threading.lock () threads = [] # thread1 = myThread(1) "Thread-1", 1) thread2 = myThread(2, "Thread-2", Thread.append (thread1) Thread.append (thread2) # Wait for all threads to complete In Threads: t.join() print (" Exit threads ")Copy the code

Execute the above program, and the output result is:

Thread priority Queue

Python’s Queue module provides synchronous, thread-safe Queue classes, including FIFO (first in, first out) Queue, LIFO (last in, first out) Queue, and PriorityQueue.

These queues implement locking primitives that can be used directly in multiple threads, and queues can be used for synchronization between threads. Common methods in modules are as follows:

  • Queue.qsize() returns the size of the Queue
  • Queue.empty() returns True if the Queue is empty, False otherwise
  • Queue.full() returns True if the Queue is full, False otherwise
  • Queue. Full corresponds to maxsize
  • Queue.get([block[, timeout]]) Gets the Queue, timeout wait time
  • Queue. Get_nowait () quite a Queue. Get (False)
  • Queue. Put (item) Write to Queue, timeout Wait time
  • Queue. Put_nowait (item) equivalent to Queue. Put (item, False)
  • Queue.task_done() After a task has been completed, the queue.task_done () function sends a signal to the Queue that the task has completed
  • Queue.join() actually means to wait 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 (" start thread: "+ self.name) process_data(self.name, self.q) print (" Start 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", Threadthread.lock () workQueue = queue.queue (10) threads = [] threadID = 1 threadList: thread = myThread(threadID, tName, Thread.start () thread.append (thread) threadID += 1 Workqueue.put (word) queuelock.release () # For threads: t.join() print (" exit main thread ")Copy the code

Results of the above procedures: