Multithreading is similar to executing multiple different programs at the same time. Multithreading has the following advantages:
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.
Threads are different from processes during execution. Each independent process has a program entry, 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).
While other threads are running, threads can be temporarily suspended (also known as sleep) — this is the concession of threads.
Thread is a branch that executes program code during program running. Each running program has at least one thread
Create multithread: thread Call start in the thread module_ new_ Thread() function to generate a new thread thread.start_new_thread ( function, args[, kwargs] ) Function - thread function. Args - the parameter passed to the thread function. It must be of type tuple. Kwargs - optional parameter. Python multithreading on start()
import threading import time def task(): time.sleep(1) Print ("current thread:", threading. Current_thread(). Name) if__name__ =='__main__': for_inrange(5): sub_thread = threading.Thread(target=task) sub_thread.start()
Execution between threads is out of order
The main thread will wait for all child threads to end
The main thread will wait until all sub threads are finished. If necessary, you can set the guard main thread
Characteristics of multithreading (sharing global variables)
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(): 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(): Set the thread name.
A custom thread cannot specify a target because all tasks in the custom thread are executed in the run method
The startup thread calls the start method uniformly. Do not call the run method directly, because it does not use sub threads to execute tasks
import threading #Custom thread class classMyThread(threading.Thread): #Get the parameters of the receiving task through the construction method def__init__(self, info1, info2): #Call the constructor of the parent class super(MyThread, self).__init__() self.info1 = info1 self.info2 = info2 #Define tasks related to custom threads def test1(self): print(self.info1) deftest2(self): print(self.info2) #Perform related tasks through the run method def run(self): self.test1() self.test2()
Create custom thread
my_ Thread = myThread ("test 1", "test 2")
Multiple threads can share global variables
Multi threads share global variables, which is convenient to share data among multiple threads
Because multiple threads operate on global variables at the same time, resource competition is easy to occur
import threading #Define global variables g_num =0 #Loop once to add 1 to the global variable defsum_num1(): for i in range(1000000): globalg_num g_num +=1 print("sum1:", g_num) #Loop once to add 1 to the global variable defsum_num2(): for i in range(1000000): global g_num g_num +=1 print("sum2:", g_num) if__name__ =='__main__': #Create two threads first_thread = threading.Thread(target=sum_num1) second_thread = threading.Thread(target=sum_num2) #Start thread first_thread.start() #Start thread second_thread.start()
We can see that multiple threads operate data on global variables at the same time
Analysis of possible data errors caused by multithreading operating global variables at the same time
Two threads first_ Thread and second_ Thread must be on the global variable G_ Num (0 by default) adds 1. However, due to the simultaneous operation of multiple threads, the following situations may occur:
In G_ When num = 0, first_ Thread get G_ num=0。 At this time, the system sets first_ Thread is scheduled to the “sleeping” state, and second_ Thread changes to the “running” state, and T2 also gets G_ num=0
Then second_ Thread adds 1 to the obtained value and assigns it to g_ Num so that G_ num=1
Then the system puts second_ Thread is scheduled as “sleeping”, and the first_ Thread is changed to “running”. Thread T1 assigns its previous 0 plus 1 to g_ num。
This leads to the first_ Thread and first_ Threads are all right for G_ Num plus 1, but the result is still G_ num=1
Solution to multi thread resource competition
Thread synchronization: ensure that only one thread can operate global variable synchronization at the same time: cooperate with the pace and run in a predetermined order. If you finish, I’ll say it again. It’s like a walkie talkie in real life
If multiple threads operate on the same global variable at the same time, the problem of resource contention data error may occur
Thread synchronization can solve the problem of resource contention and data errors, but in this way, multiple tasks become single tasks.
The locking variable is defined in the threading module. This variable is essentially a function that can easily handle locking:
mutex = threading.Lock()
import threading #Define global variables g_num =0 #Create global mutex lock = threading.Lock() #Loop once to add 1 to the global variable def sum_num1(): #Lock lock.acquire() for i in range(1000000): global g_num g_num +=1 print("sum1:", g_num) #Release lock lock.release() #Loop once to add 1 to the global variable def sum_num2(): #Lock lock.acquire() for iin range(1000000): global g_num g_num +=1 print("sum2:", g_num) #Release lock lock.release() if __name__ =='__main__': #Create two threads first_thread = threading.Thread(target=sum_num1) second_thread = threading.Thread(target=sum_num2) #Start thread first_thread.start() second_thread.start()
Tip: with the mutex lock, we can’t decide which thread grabs the lock. Which thread grabs the lock and which thread executes first. Threads that don’t grab the lock need to wait
#Coupled with the mutex lock, multitasking instantly becomes a single task, and the performance will decline, that is, only one thread can execute at the same time
When a thread calls the acquire () method of the lock to obtain the lock, the lock enters the “locked” state.
Only one thread can acquire a lock at a time. If another thread attempts to obtain the lock at this time, the thread will change to the “blocked” state, which is called “blocking”. The lock will enter the “unlocked” state until the thread owning the lock calls the release () method of the lock to release the lock.
The thread scheduler selects one of the threads in the synchronous blocking state to obtain the lock and put the thread into the running state.
It ensures that a piece of key code can only be completely executed by one thread from beginning to end
Disadvantages of lock:
Multithreaded execution becomes a piece of code with locks. In fact, it can only be executed in single thread mode, which greatly reduces the efficiency
If the lock is not used well, it is easy to deadlock
import threading import time #Create mutex lock = threading.Lock() #Take values according to subscripts to ensure that only one thread can take values at the same time def get_value(index): #Lock lock.acquire() print(threading.current_thread()) my_list = [3,6,8,1] #Judge subscript release out of bounds if index >=len(my_list): Print ("subscript out of bounds:", index) return value = my_list[index] print(value) time.sleep(0.2) #Release lock lock.release() if __name__ =='__main__': #Simulate a large number of threads to perform value taking operations for i in range(30): sub_thread = threading.Thread(target=get_value,args=(i,)) sub_thread.start()
When using mutex locks, you should pay attention to deadlock and release locks in appropriate places
Once a deadlock occurs, the application will stop responding
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. These queues implement lock primitives and can be used directly in multiple threads. Queues can be used to realize 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
Thread, have you failed?