content
Introduction to threads
- A thread is a processing task unit lower than a process . It is sometimes called a lightweight process. It is the smallest unit of program execution flow. It can be said that multiple threads can be created in a process to process multiple tasks.
- The components of a thread can be divided into thread ID, current system instruction pointer, register set, stack combination.
- A thread is an entity in a process, the basic unit independently scheduled and dispatched by the system. A thread does not own private system resources. There are one or more processes in the operating system, and each process has its own exclusive CPU resources. Resource sharing is not possible, but if CPU resource sharing is required now, it can be achieved through threading technology
- Threads are more lightweight control units than processes, and the cost of creating and destroying threads is smaller. Using threads can improve the processing performance of processes
- Running multiple threads in a single program to complete different work at the same time is called multi-threading technology
Modern processors are multi-core multi-threaded execution programs that appear to be running at the same time, but in fact, the CPU quickly switches execution between multiple threads.
The threading module implements multithreading
Use the Thread class in
it using the format:
t=threading.Thread(target=None,name=None,args=())
parameter | describe |
---|---|
target | The function or method called when the thread starts |
name | thread name |
args | The parameters that the function needs to pass in (tuple form) |
The main method of the Thread object
method | Introduction |
---|---|
run() | Methods used to represent thread activity |
start() | start thread |
join() | wait until thread terminates |
isAlive() | Determine if a thread is active |
getName() | return thread name |
setName() | set thread name |
Create a thread functionally
When creating a thread, you only need to pass in an execution function and the parameters of the function. The following example uses the Thread class to generate two child threads and wait for them to end
import threading
import time,os,random,math
def printnum(num):
for i in range(num):
print(f'{
threading.current_thread().getName()},{
i}')
time.sleep(1)
if __name__=='__main__':
t1=threading.Thread(target=printnum,args=(2,),name='thread1')
t2=threading.Thread(target=printnum,args=(3,),name='thread2')
t1.start()
t2.start()
t1.join()
t2.join()
print(f'{
threading.current_thread().getName()}线程结束')
operation result
Create thread class
Create a subclass of Thread directly to create a thread object for multithreading
import threading,time
class mythread(threading.Thread):
def __init__(self,name,num):
threading.Thread.__init__(self)
self.name=name
self.num=num
def run(self): # 线程启动后自动调用run()方法
for i in range(self.num):
print((f'{
threading.current_thread().getName()},{
i}'))
time.sleep(1)
if __name__=='__main__':
t1=mythread('thread1',3)
t2=mythread('thread2',2)
t1.start()
t2.start()
t1.join()
t2.join()
print((f'{
threading.current_thread().getName()}线程结束'))
operation result
Two functions in the threading module obtain active thread information
function | describe |
---|---|
active_count() | Get the current number of active threads |
encumerate () | Get active thread information, return a list sequence |
Daemon thread
The main thread needs to wait for the sub-thread to finish executing before continuing. If the sub-thread does not use the join() function, the main thread and the sub-thread will run together, and there is no dependency between them.
Usage format: 线程对象.setDaemon(True)
In multi-threaded programming, if the sub-thread is set to The daemon thread designated as the main thread will be destroyed after waiting for the main thread to run. At this time, the living thread must exist for the daemon thread to run.
import threading
import time
def run(taskname):
print(f'任务-{
taskname}')
time.sleep(2)
print(f'任务-{
taskname}执行完毕')
if __name__=='__main__':
for i in range(3):
thread=threading.Thread(target=run,args=(f'{
i}',))
thread.setDaemon(True)
thread.start()
print(f'线程结束:{
threading.current_thread().getName()},当前线程数量为:{
threading.active_count()}')
Running results
You can see that after the main thread is executed, the program exits without waiting for the daemon thread to finish executing.
thread terminated
The threading module does not provide a thread termination method, nor does it support directly stopping the thread. The threads created by Thread() are independent of each other. If the sub-thread is started in the main thread, the two are also independent
thread termination methods.
If you want to Forcibly terminate the child thread at the same time as the main thread is terminated, the easiest way is to set the child thread as a daemon thread, this is one way to stop the thread, there are other ways to stop the child thread
- Generate a thread object, put the complex business in the loop, set a stop flag for the thread object, and exit the loop once the flag reaches a predetermined value, so that the thread can be stopped
import threading
import time
class testthread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self._running=True # 设置线程标志位
def terminate(self):
self._running=False
def run(self):
count=1
threadname=threading.current_thread().getName()
while self._running:
print(f'线程名称:{
threadname},次数:{
count}')
count+=1
time.sleep(1)
if __name__=='__main__':
t1=testthread()
t1.start()
time.sleep(3) # 等待三秒,期间次数加3
t1.terminate() # 修改标志位的值,停止子线程
print('主线程结束')
2. Called by the ctypes module, an exception is reported in the child thread, so that the child thread exits
Multi-threaded locking mechanism
The problem solved by the locking mechanism
There will be data security problems when multiple threads modify global variables at the same time. Simply put, it is possible that multiple threads modify data successively, resulting in inconsistent data, also known as "dirty data".
Case 1: Two threads modify one data at the same time
import threading
num=10
def change_num(m,counter):
global num
for i in range(counter):
num+=m
num-=m
if num!=10: # 创建一个警示语句,如果该语句被执行则说明
print(f'num的值为:{
num}')
break
if __name__=='__main__':
t1=threading.Thread(target=change_num,args=(10,500000),name='线程1')
t2=threading.Thread(target=change_num,args=(10,500000),name='线程2')
t1.start()
t2.start()
t1.join()
t2.join()
print(f'线程结束:{
threading.current_thread().getName()}')
Running results
From the running results, we can find that the change_num() function should ensure that the value of num is always 10 by adding and then subtracting, but the result is not the case. In fact, the scheduling of threads is determined by the system to start two threads alternately at the same time, as long as The number of times is enough, the result of num is not necessarily 10
Introduction to mutex locks
For thread safety, mutex locks need to be used. When a thread wants to modify a data resource, it will be locked first. At this time, the state of the resource is "locked", and other threads cannot change it until the thread releases the resource. , other threads can lock the resource again, the
meaning
of the mutex lock The mutex lock ensures that only one thread performs the write operation at a time, thus ensuring the correctness of the data
The core code of the lock mechanism
# 创建一个锁对象
lock1=threading.Lock()
# 锁定
lock1.acquire()
# 释放
lock1.release()
Case 2: Two threads modify the same data (locking)
import threading
num=10
lock=threading.Loak()
def change_num(m,counter):
global num
for i in range(counter):
lock.acquire() # 获取锁
num+=m
num-=m
lock.release() # 获得锁的线程用完后一定要释放锁,否则其它线程就会一直等待下去,从而成为死线程
if num!=10:
print(f'num的值为:{
num}')
break
if __name__=='__main__':
t1=threading.Thread(target=change_num,args=(10,500000),name='线程1')
t2=threading.Thread(target=change_num,args=(10,500000),name='线程2')
t1.start()
t2.start()
t1.join()
t2.join()
print(f'线程结束:{
threading.current_thread().getName()}')
operation result
In the second case above, we added a lock mechanism to the change_num() function, so that when a process executes the change_num() function, it will acquire the lock, and other threads will wait until the lock is acquired, so that two threads When modifying the global variable num, there will be no conflict, ensuring data security