多线程
1. 程序,进程,线程
1、程序是指一组指示计算机或其他具有信息处理能力装置执行动作或做出判断的指令,通常用某种程序设计语言编写,运行于某种目标计算机体系结构上。程序的通俗定义就是:一段可执行的代码
2、进程是计算机中的软件程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。一个运行起来的程序就是一个进程
3、线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有操作系统资源,但是该线程可与同属进程的其他线程共享该进程所拥有的全部资源。
进程是程序的实体,而线程又是进程的实体。进程又是线程的容器。
2. 线程的实现
python两种方法实现线程: Thead类 自定义线程类
01 线程类Thread
-
threading 模块中Thread类
- 属性:
- name 线程的名称
- target 线程要执行的函数名
- args 函数的实参列表
- 方法:
- start( ) 启动线程的方法
- run( ) 运行线程 的方法
- 属性:
-
线程执行的流程:
- (1)线程对象 调用 start() 方法 启动线程 等待CPU调度
- (2)当CPU调度到此线程时,线程对象自动调用run()方法运行线程
02 线程实现方式
- 实现思路: 创建Thread类的实例, 调用start() 启动线程
- 线程执行的核心: 调用函数
- 准备:一个函数
- 第一步:创建线程对象
- 语法 : 线程对象= Thread( target =函数名, args=(函数的实参列表,))
- target 线程函数名 ----线程要执行的函数
- args 线程函数的实参列表,是元组类型
- 第二步: 启动线程
- 线程对象.start()
- 示例: 使用线程 模拟 两个线程在CPU调度下,交替运行
import time
from threading import Thread
#功能方法:数数(从1数到5)
def show_num(tname):
for i in range(6):
print(tname, i)
time.sleep( 3 )#暂停3秒,控制输出5个数需要稍长的时间才能完成
if __name__ == '__main__':
print("111111111111111111")
#创建线程,再启动线程
t1 =Thread(target= show_num, args=("第一个线程",))
t1.start()
t2 =Thread(target=show_num,args=("第二个线程",))
t2.start()
print("222222222222222222")
'''
线程在CPU的调度下, 交替执行 ----多个线程争抢CPU
小结:
1. 使用 Thread类 创建线程的语法
2. 理解 就绪的线程被CPU的调度后,自动执行 run()
'''
- 小结 线程执行的流程:
-- 创建线程对象----------- 线程状态:创建状态
-- 线程对象调用start() ---------- 线程状态:就绪状态, 等CPU来调度
-- 当CPU调度到它时,线程对象调用run() 会执行线程函数----------线程状态:运行状态
03.多个线程共享全局变量
- 当多个线程共享 全局变量时,可能导致全局变量的值会出现不正常
import time
from threading import Thread
from threading import Lock
num=0 #全局变量
def set_num( tname ):
global num
num+=1
time.sleep(0.3) #模拟num 加1减1的操作会花费较长的时间
num-=1
print(tname,num)
if __name__ == '__main__':
t1 =Thread(target=set_num,args=('t1',))
t1.start()
t2 = Thread(target=set_num,args=('t2',))
t2.start()
t3 = Thread(target=set_num,args=('t3',))
t3.start()
#理想状态:多个线程操作完后,全局变量的数据还是0
#运行结果:每次运行都有不同的结果, 这是因为 线程由CPU调度,可能一个线程没有执行完就暂停切换到另一个线程执行。
#防止共享数据修改产生不正确数据,就需要在线程更改数据前锁定这个数据,修改完解锁后其它线程才能再更改这个数据
#使用锁 锁定资源(一段代码),这时其它线程不能访问,直到释放锁后 其它线程才可访问
# 使用threading模块中Lock类 创建锁 lock = Lock()
# 加锁 lock.acquire()
# 释放锁 lock.release()
import time
from threading import Thread
from threading import Lock
num=0 #全局变量
''''
# 使用threading中Lock 创建锁 lock =Lock()
# 加锁 lock.acquire()
# 释放锁 lock.release()
'''
lock =Lock()
def set_num( tname ):
global num
lock.acquire() #加锁
num+=1
time.sleep(0.3)
num-=1
lock.release() #解锁
print(tname,num)
#当某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其它线程不能更改,直到该线程释放资源,其他线程才能修改。
- 互斥锁: 每次只有一个线程进行写入操作,保证了多线程情况 共享数据更改的正确性
- 定义锁: mylock=threading. Lock()
- 加锁: mylock.acquire()
- 解锁: mylock.release()
#==========================使用 自定义线程类 创建3个线程==============================
import time
from threading import Thread
from threading import Lock
num = 0 # 全局变量
lock =Lock()
class MyThread(Thread):
def run(self) -> None:
self.set_num()
def set_num( self ):
global num
lock.acquire() #加锁
num+=1
time.sleep(0.3)
num-=1
lock.release() # 解锁
print(self.name,num)
if __name__ == '__main__':
t1 =MyThread(name='t1')
t2 =MyThread(name='t2')
t3 =MyThread(name='t3')
t1.start()
t2.start()
t3.start()
04 守护线程与阻塞线程
-
# 什么都不设置,按顺序执行主线程,当主线程执行完毕,有未执行完毕的子线程,子线程继续执行 def demo(num): # 循环输出1-10,每次暂停0.1s for i in range(num): print(i + 1) time.sleep(0.1) if __name__ == '__main__': obj = threading.Thread(target=demo, args=(10,)) obj.start() print('下载完毕')
-
# 想让主线程在子线程执行完毕后在执行--->阻塞线程(执行子线程完毕之后最后在执行主线程)obj.join() def demo(num): # 循环输出1-10,每次暂停0.1s for i in range(num): print(i + 1) time.sleep(0.1) if __name__ == '__main__': obj = threading.Thread(target=demo, args=(10,)) obj.start() 阻塞主线程 obj.join() print('下载完毕')
-
# 想让主线程执行完毕后马上结束子线程--->守护线程(执行完主线程后马上去中断子线程) obj.daemon = True def demo(num): # 循环输出1-10,每次暂停0.1s for i in range(num): print(i + 1) time.sleep(0.1) if __name__ == '__main__': obj = threading.Thread(target=demo, args=(10,)) obj.daemon = True obj.start() print('下载完毕')
05 线程状态(了解):
-
线程生命周期:
-
1.新建状态:
- 创建线程对象 新建状态。
-
2.就绪状态:
- 调用 start() 方法后,就绪状态(随时等待 CPU 调度)
-
3.运行状态:
- 线程得到了 CPU调度,运行状态 (执行 target 参数的目标函数或者 run() 方法)
-
4.阻塞状态:
- 对于获得 CPU 调度却没有执行完毕的线程,就会进入阻塞状态
- sleep( n ) -----------休眠时间到,转到就绪状态
- 等待锁--------------等待前面线程释放解锁,转到就绪状态
-
- 死亡状态
-
线程具有就绪,等待和运行三种基本状态和状态间转换关系