一、多任务
并行:同时执行多个任务
并发:交替执行任务
二、进程
1、概述:是 操作系统CPU 资源分配 的最小单位
2、多进程作用:提高 执行效率
3、创建进程
# 导包
import multiprocessing
子进程对象 = multiprocessing.Process(target=...,name=...,args=...,kwargs=...)
进程对象.start( )
4、进程编号:唯一标识一个进程
# 获取当前进程
os.getpid()
mp.current_process().pid
# 获取父进程
os.getppid()
5、注意点
进程之间 不共享 全局变量(进程间数据是 相互隔离的)
主进程会等待 所有子进程 执行结束再结束
6、例题
需求:请使用多进程形式完成:一边写代码、一边听音乐。
# 1、导包
import multiprocessing, time
# 2、定义 写代码 函数
def coding():
for i in range(5):
print(f'敲了{i}行代码')
time.sleep(0.5) # 休眠一下,否则数据太少,体现不出结果
# 3、定义 听音乐 函数
def music():
for i in range(5):
print(f'听了{i}首音乐')
time.sleep(0.5)
# 4、使用多进程完成;
if __name__ == '__main__':
p1 = multiprocessing.Process(target = coding) # 调用multiprocessing
p2 = multiprocessing.Process(target = music)
p1.start() # 运行子进程
p2.start()
三、线程
1、概述:依附于进程,是CPU调度的基本单元
2、CPU调度资源策略
抢占式调度:谁抢到谁执行,可能数据乱序
均分时间片:每个线程获取的CPU时间都是固定的,一致的
3、实现格式
# 1、导包
import threading
子线程对象 = Thread(target=...,name=...,args=...,kwargs=...)
# 2、获取当前线程
线程对象=threading.current_thread()
4、特点
1. 执行顺序:无序
2. 主线程会 等待 所有子线程 执行结束再结束
3. 线程间 数据可以共享
5、例题
需求:请使用多线形式完成:一边写代码、一边听音乐。
# 1、导包
import threading, time
# 2、定义 编码 函数
def coding():
for i in range(5):
print(f'敲了{i}行代码')
time.sleep(0.5)
# 3、定义 听音乐 函数
def music():
for i in range(5):
print(f'听了{i}首音乐')
time.sleep(0.5)
# 4、在 main函数中测试
if __name__ == '__main__':
t1 = threading.Thread(target=coding)
t2 = threading.Thread(target=music)
t3 = threading.Thread(target=chat)
t1.start()
t2.start()
t3.start()
6、让主进程 结束时,子进程 立即结束的2个方式
1. 守护进程 or 线程
当非守护进程结束时,守护进程立即结束
设置:子进程.daemon=True
举个例子吧~
import threading, time
def work():
for i in range(5):
print(f'工作{i}小时')
time.sleep(0.3)
if __name__ == '__main__':
t1 = threading.Thread(target=work, daemon=True) # 守护线程
t1.start()
time.sleep(1)
print('主线程结束')
2. 手动结束:子进程.terminate()
僵尸进程,不建议写,虽然关闭但依然占用资源
子线程不能手动结束
7、互斥锁
1. 作用:对共享数据进行锁定,保证同一时刻只有一个线程去操作
2. 流程:创建锁:mutex = threading.Lock( )
加锁:mutex.acquire( )
释放锁:mutex.release( )
3. 线程同步:也叫线程安全,即同一时刻,只有1个线程对资源有操作权,通过加锁实现
# 1. 导包
import threading, time
# 创建(互斥)锁
mutex = threading.Lock()
# 2. 定义全局变量
my_num = 0
# 3. 定义函数, 对 my_num累加 100W次
def add_sum1():
# 加锁
mutex.acquire()
for i in range(1000000):
global my_num # 修饰为全局变量.
my_num += 1
# 解锁
mutex.release()
print(f'add_sum1函数: {my_num}') # 1000000
# 4. 定义函数, 对 my_num累加 100W次
def add_sum2():
# time.sleep(3)
# 加锁
mutex.acquire()
for i in range(1000000):
global my_num # 修饰为全局变量.
my_num += 1
# 解锁
mutex.release()
print(f'add_sum2函数: {my_num}') # 2000000
# 主线程
if __name__ == '__main__':
# 5. 创建线程
t1 = threading.Thread(target=add_sum1)
t2 = threading.Thread(target=add_sum2)
# 6. 开启线程.
t1.start()
t2.start()
8、死锁:一直等待对方释放锁的情景
# 1、导包
import threading, time
# 2、创建锁
mutex = threading.Lock()
# 3、定义 编码 函数
def coding():
mutex.acquire()
for i in range(5):
print(f'敲了{i}行代码')
time.sleep(0.5)
# mutex.release() # 没有释放锁
# 4、定义 听音乐 函数
def music():
mutex.acquire()
for i in range(5):
print(f'听了{i}首音乐')
time.sleep(0.5)
# mutex.release() # 没有释放锁
# 5、主线程
if __name__ == '__main__':
# 创建线程
t1 = threading.Thread(target=coding)
t2 = threading.Thread(target=music)
# 开启线程
t1.start()
t2.start()
四、进程和线程的对比
1. 进程可以多核,资源开销大;线程资源开销小,不可以多核
2. 进程之间 不共享 全局变量,线程 共享
3. 进程是操作系统 资源分配 的最小单位,线程是 CPU调度 的基本单元
4. Python中 多进程开发 比 单进程多线程开发 稳定性要强