一. 多任务
操作系统可以同时运行多个任务
二. 单核CPU实现多任务的原理
操作系统轮流让多个任务交替执行 比如QQ执行了2us ,在切换到微信 执行了2us,在切换到其他,表面看起来像是在同时执行,是因为cpu的调度太快
三. 多核cpu实现多任务的原理
如果任务量多于核心数依然是让多个任务去交替执行
(1)并发
看起来像是同时执行 其实还是cpu去调度
(2) 并行
真正的一起执行 任务量 小于等于cpu的核心数
注意: 并发是逻辑上的同时执行,并行是物理上的同时执行
四. 进程模块Process
(1) 单进程
一个代码执行完毕 需要等待下一个代码执行完 在继续往下执行
(2) 实现多进程
__name__ 是windows 上实现多进程的方式 以防止模块被导入 造成多个进程执行的混乱
(3) 开启多进程
from multiprocessing import Process
创建子进程
p = Process(target=函数名)
target 执行子任务的函数名称
(4) 进程等待
默认是主进程 继续往下执行 执行完毕结束
主进程等待子进程
进程名.join()
当子进程执行完毕 主进程继续执行
五. 进程池
当启动大量进程的时候 使用进程池
from multiprocessing import Pool
创建进程池
pool = Pool([进程并发数量]) # 当不传参的时候 并发数为核心数
创建子进程 放入进程池 统一管理
apply_async(func,args=(参数,)) # 并发执行 非阻塞模式
apply(func,args=(参数,)) # 非并发执行 阻塞模式
pool.close() # 进程池关闭 代表 不在向进程池加入子进程
pool.join() # 主进程等待子进程 执行完毕在此执行
注意:
进程池:调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool, join函数等待所有子进程结束
获取子进程的返回值
def demo(x): # print(x) # time.sleep(2) return x # 将出入进来的参数进行返回 if __name__ == '__main__': print(cpu_count()) # 4 pool = Pool() for i in range(10): res = pool.apply_async(demo, args=(i,)) print(res) # 返回结果对象,此刻并发变成了阻塞一个一个执行 print(res.get()) # 获取返回值 # res = pool.apply(demo, args=(i,)) # print(res) # 直接返回结果 pool.close() # 不在向进程池放进程 pool.join() # 进程等待
map方法
如果有大量的数据添加到进程池里 如果使用map会非常的方便
第一个参数传入方法 第二个方法传入列表
实例:
def demo(x): time.sleep(2) print(x) if __name__ == '__main__': pool = Pool() urlList = ['http://www.baidu.com','http://www.taobao.com'] pool.map(demo,urlList) # 并发执行
六. 进程通信之 队列
from multiprocessing import Queue
# 创建队列 que= Queue() # 压入数据 que.put() # 获取数据 que.get(block=True) # block默认为True 阻塞一直等待 传入False 非阻塞模式 若为空 会抛出异常 # 队列常用函数 que.empty() # 判断队列是否为空 返回bool 进程名.terminate() # 强制终止子进程
注意: 给子进程终止法方法不应该存在 子进程名.join() 因为主进程一直会等待,终止子进程的方法不会被执行
p.daemon = True # 设置子进程 随着主进程结束而结束
七. 自定义进程类
实例:
class MyClass(Process): def __init__(self,arg): Process.__init__(self) self.arg = arg def run(self): print(self.arg) # 调用类的属性 print("走了") if __name__ == '__main__': pro = MyClass() pro.start() # 会自动调用run方法
注意:
- 只有__init__和run方法 可以重写 但是init需要调用父类的初始化方法
- 将当前的自定义类实例化后调用start方法 会自动调用run方法
- 如果开启多个子进程需要将当前自定义类多次实例化