python学习之进程
1 进程介绍 1.1 什么是进程? 含义:是操作系统进行资源分配和调度的基本单位,是操作系统结构的基础 一个正在运行的程序或者软件就是一个进程 程序跑起来就成了进程 注意:进程里面可以创建多个线程,多进程也可以完成多任务 1.2 进程的状态(三种) 1. 就绪状态: 运行状态都已经满足,正在等待CPU执行 2. 执行状态: cpu正在执行其功能 3. (等待)阻塞状态: 等待某些条件满足,如一个程序sleep了,此时就处于等待状态
import time print("我是milk") #执行状态 sex = input("输入性别: ") #光标闪动,等待用户输入 print(sex) #执行状态 time.sleep(1) #延时1秒,等待状态
2.进程语法结构
multiprocessing模块提供了Process类代表进程对象
2.1 Process 类参数
1.target:执行的目标任务名,即子进程要执行的任务
2.args: 以元组的形式传参
3.kwargs: 以字典的形式传参
2.2 方法
1.start():开启子进程
2.is_alive():判断子进程是否还活着,存活返回Ture,死亡返回False
3.join():主进程等待子进程执行结束
2.3 属性:
name: 当前进程的别名,默认Process-N
pid: 当前进程的进程编号
import time from multiprocessing import Process import os def sing():# os.getpid():获取当前进程编号# os.getppid():获取当前父进程的编号print(f"sing的进程编号:{os.getpid()},父进程编号:{os.getppid()}")# 父进程的id就是py文件主进程的idprint("唱歌") def dance():print(f"dance的进程编号:{os.getpid()},父进程编号:{os.getppid()}")print("跳舞") if __name__ == '__main__':# 创建子进程# 修改子进程名第一种方式,没有修改,默认为Process-Ns = Process(target=sing,name="子进程一")d = Process(target=dance,name="子进程二")# 开启子进程s.start()d.start()# 修改子进程名第一种方式s.name='p1'd.name = 'p2'#访问name属性print("s: ",s.name)print("d: ",d.name)#查看子进程的进程编号print("s.pid: ",s.pid)print("d.pid: ",d.pid)print(f"主进程pid:{os.getpid()},主进程的父进程id:{os.getppid()}")
cmd命令提示符窗口输入tasklist可以查看电脑里面的进程的命令
ctrl+F查找
pycharm软件的进程编号就是主进程的父进程的进程编号
def eat(name):print(f"{name}在吃饭") def sleep(name):print(f"{name}在睡觉") if __name__ == '__main__':p1 = Process(target=eat, args=('milk',))p2 = Process(target=sleep, args=('alice',))p1.start()p1.join()
当调用 p1.start() 后,子进程 p1 进入就绪状态(等待 CPU 调度执行),之后会转为运行态。
此时主进程会继续向下执行代码,直到遇到 p1.join()。
调用 p1.join() 后,主进程会进入阻塞状态(而非单纯的 “等待状态”),
直到子进程 p1 执行结束(终止态),主进程才会解除阻塞,继续执行后续代码。
p2.start() p2.join() print("p1存活状态: ",p1.is_alive()) print("p2存活状态: ",p2.is_alive())
2.4进程间不共享全局变量
为什么要有if __name__ == '__main__':
1.防止别人导入文件的时候执行main里面的方法
2.防止windows系统递归创建子进程
Linux/macOS 使用 fork 机制:创建子进程时,直接复制主进程的内存空间,
但子进程会从 start() 之后的代码开始执行,不会重复执行主进程创建子进程前的代码。
Windows 没有 fork,使用 spawn 机制:创建子进程时,会重新加载整个 Python 文件,
从头到尾执行一遍代码。
如果没有 if __name__ == '__main__':,Windows 中创建子进程的代码
(比如 Process 定义和 start())会被子进程再次执行,子进程又会创建新的子进程,形成无限递归,
最终导致程序崩溃。
而 __name__ 变量在主进程中为 '__main__',在子进程(被加载的模块)中为模块名,
因此通过这个判断,可以确保创建子进程的代码只在主进程中执行,避免递归。
对于linux和mac系统,主进程执行的代码不会将进程去进行拷贝,
但对于windows电脑,主进程执行的代码也会进行拷贝执行,对于
windows来说,创建子进程的代码,如果进程进程拷贝(复制),执行就
相当于递归,无限制的去创建子进程,如果要解决windows递归创建子进程,
通过判断他是否是这个主模块
import time from multiprocessing import Process li = [] def wdata():for i in range(5):li.append(i)time.sleep(1)print('写入的数据是: ',li) def rdata():print('读取的数据是: ',li) if __name__ == '__main__':wd = Process(target=wdata)rd = Process(target=rdata)wd.start()wd.join()rd.start()
写入的数据是: [0, 1, 2, 3, 4] 读取的数据是: [] 读取一直是空的,进程不共享全局变量
2.5进程间的通信
Queue(队列) q.put():放入数据 q.get():取出数据 q.empty():判断队列是否为空 q.qsize():返回当前队列包含的消息数量 q.pull():判断队列是否满了
#导入模块 from multiprocessing import Process,Queue import time # 初始化一个队列对象 q = Queue(3) #最多可以接受三条消息,没写或者是负值就代表没有上限,直到内存的尽头 q.put('你好') q.put('我是Mile') print("是否满了: ",q.full()) q.put('很高心认识你') # q.put('4') # 最多接受三条数据,不能多了 # print(q.qsize()) print(q.get()) #获取队列的一条消息,然后将其从队列中移除 print(q.get()) # print(q.empty()) print(q.get()) # print(q.empty()) # print(q.qsize())
from multiprocessing import Process,Queue import time li = ['mike','alice','lisa','liming'] def wdata(q1):for i in range(5):print(f'{i}已经被放入')q1.put(i)time.sleep(1)print('写入的数据是: ',li) def rdata(q2):while True:# 判断队列是否为空if q2.empty():breakelse:print("取出的数据是", q2.get())print('读取的数据是: ',li) if __name__ == '__main__':q = Queue()wd = Process(target=wdata,args=(q,))rd = Process(target=rdata,args=(q,))wd.start()wd.join() #等待队列中的元素放入完成rd.start()