上节课的内容:
01 操作系统
系统接口
操作系统内核
02 多道技术
空间上的复用:复用内存的空间
1、多道/个程序的代码都存入内存
2、内存空间是物理隔离的
时间上的复用:复用cpu的时间
cpu的切换
1、任务的运行遇到IO阻塞=》提升效率
2、任务占用cpu时间过长=》降低效率
如果我们的核心目标是想让多个任务并发起来:那么无论是遇到IO还是没有遇到IO都应该让cpu在多个任务之间切换起来
如果我们的核心目标是想让多个任务的运行效率提升:我们应该只控制一个任务在遇到IO情况下切换到另外一个任务
今日内容:
01 进程理论
并发、并行、串行
进程运行的三种状态:(运行态、就绪态)非阻塞态、阻塞态
调用任务的两种方式:同步调用、异步调用
02 进程的使用
process
=================================> 进程理论 <=====================================
#1、什么是进程
进程是一个程序运行的过程
#2、什么是程序
程序就是一堆代码文件
#3、串行、并发、并行
串行:多个任务依次运行,一个运行完毕再运行下一个
并行:多个任务是真的在同时运行,只有多个cpu才有并行的概念
并发:多个任务看起来是在同时运行
#4、进程的创建
windows:CreateProcess
linux:fork
#5、进程的终止
1. 正常退出
5. 被其他进程杀死(taskill /F /PID 3333)
#6、程序运行的三种状态
运行态
就绪态
阻塞态
优化程序效率的核心法则:降低IO操作(硬盘IO、网络IO)
内存》本地硬盘-》网络IO
#7、
进程运行的三种状态:(运行态、就绪态)非阻塞态、阻塞态
调用任务的两种方式:同步调用、异步调用
=================================> 进程应用 <=====================================
import os
import time
print(os.getpid())
time.sleep(1000)
开启进程的方式一:
import time
import os
from multiprocessing import Process
def task(n, tag):
print("%s is running" % tag)
time.sleep(3)
print("%s is done" % tag)
if __name__ == '__main__':
p1 = Process(target=task, args=(5, "子进程1"))
p2 = Process(target=task, args=(3, "子进程2"))
p3 = Process(target=task, args=(2, "子进程3"))
p1.start()
p2.start()
p3.start()
print("主:%s。。。" % os.getpid())
# 开启进程的方式二:
import time
import os
from multiprocessing import Process
class Myprocess(Process):
def __init__(self,tag):
super().__init__()
self.tag = tag
def run(self) -> None:
print("%s is running" % self.tag)
time.sleep(3)
print("%s is done" % self.tag)
if __name__ == '__main__':
p1 = Myprocess("进程1")
p2 = Myprocess("进程2")
p3 = Myprocess("进程3")
p1.start() # p1.run()
p2.start() # p2.run()
p3.start() # p3.run()
print("主:%s。。。" % os.getpid())
=================================> join <=====================================
import time
import os
from multiprocessing import Process
def task(n, tag):
print("%s is running" % tag)
time.sleep(n)
print("%s is done" % tag)
if __name__ == '__main__':
p1 = Process(target=task, args=(3, "子进程1"))
p2 = Process(target=task, args=(4, "子进程2"))
p3 = Process(target=task, args=(5, "子进程3"))
# start=time.time()
p1.start()
p2.start()
p3.start()
p1.join()
# p3.join()
# p2.join()
print(p1.pid)
print(p2.pid)
print(p3.pid)
# print(time.time() - start)
print("主线程:%s。。。" % os.getpid())
========================> 知识点整理 <=========================
if __name__ == '__main__' 的功能:
如果当前文件是作为主文件执行的,他里面的代码才执行。
如果是作为 模块导入(导入会先执行文件) ,则执行时不执行它里面的代码。
一个文件 <===对应===> 一个程序,
一个程序 <===对应===> 一个进程/线程(子进程)
os.getpid()
获取当前文件(程序 or 进程)的id
os.getppid()
获取当前文件(程序 or 进程)的父进程的id
ppid = parent pid
语句 Process(target = func) 的作用
def func():
...
Process(target = func)
<==== 等效于 ===>
单独开辟一个文件存放:
1、这个函数的定义代码
def func():
...
2、执行函数的语句
func()
这样依赖一个程序,开辟出来的这个文件执行时,也是一个进程,叫原程序(进程)的子进程
对应关系: 父进程 ----> 子进程
子进程是 依赖于 父进程创建的
子进程: 依赖一个文件(进程),创建出来的进程
父进程: 被依赖的是父进程
# ### 进程
import os
# 当前进程id(子进程)
res = os.getpid()
# 当前进程的父进程id
res2 = os.getppid()
from multiprocessing import Process
# (1) 进程使用的基本语法
def func():
...
# windows 里面下面这句话必须要加;
if __name__=='__main__':
# 创建子进程,返回一个进程对象(用这个进程对象,才能启动子进程),执行func任务
p = Process(target = func)
# 调用子进程
p.start()
创建带有参数的进程
Process 创建子进程,返回进程的对象p,
target 指定要执行的任务
args 指定传递的参数,args的类型是元组,多个参数之间用 逗号 隔开
cpu只用来进行 运算(算数运算 and 逻辑运算),具体的操作是cpu发号施令 由其他部件来完成的。
所以当程序的一个程序如果涉及到了非运算的操作(即 需要一定的时间才能完成的操作)的时候,则就会立马进入阻塞态,当操作完成的时候,才回到就绪态(等待 cpu轮转执行)
进程核心语句:
p = Process(......) : 得到一个进程对象, 指定了要执行的任务是什么
p.start: 为进程分配资源,创建进程,cpu轮转执行进程
先看到Process语句后面的语句效果的原因(前面的语句后打印了,后面的语句先打印了):
创建子进程的时候阻塞了,要为它开辟空间、分配空间、 分配内存、分配cpu、分配资源
分配资源的时候,一定会发生阻塞
直到资源分配完的这段时间内,先执行后面的语句
同步: 必须要等当前语句执行完毕后,才会往下面走
cpu发出命令,并且所有的命令所要进行的操作,都执行完(cpu得到命令所代表的操作都执行完毕的信号)
异步: 不等当前语句执行完,就往下走
cpu发出当前语句所具有的所有命令后,不需要等待所有的操作都执行完毕,就开始执行下一条语句
全局变量:
访问: 直接访问
修改: 在函数内部希望对全局变量进行修改,需要使用关键字global
对于多个阻塞的进程,谁先从阻塞态 变成 就绪态,就先执行谁
# (3)进程之间的数据隔离
count = 100
def func():
global count
count += 1
print(f'我是子进程count = {count}')
if __main__ == '__main__':
p = Process(target = func )
p.start()
time.sleep(1)
print(count)
# (4) 多个进程可以
异步(不等你走完,我就继续往下走了,我是额外的另一个主线)
并发(一个cpu在多个程序之间来回进行切换,由于速度太快,所以看起来多个程序在一起齐头并进,这其实是一种假象,其实都是由一个cpu搞定的)
默认程序在异步并发任务时,因为cpu调度策略问题,不一定先先执行或后执行谁,整体而言,主进程速度快于子进程,cpu遇到阻塞立刻切换到其他任务,等到进程的就绪态再切换回来.
主程序默认等待所有的子进程执行结束之后,再关闭程序,释放资源
若不等待,子进程并不方便管理,容易造成僵尸进程,在后台不停的占用系统的资源(cpu和内存等),不清楚进程的来源
进程对象 和 启动进程 这两条语句可以合并成一句:
Process(target=func,args = (1,)).start()
下面的程序执行 创建10个子进程语句 没有发生阻塞,
只不过是创建进程的过程中发生了阻塞(因为一旦涉及分配空间,就一定会发生阻塞)
if __name__ == '__main__':
for i in range(1,11):
Process(target=func,args =(i,)).start
由于 异步 的原因,打印的结果并不是按照1~10 的顺序,
谁先从 创建进程的阻塞态 编程 就绪态,cpu就先执行谁
主程序 等待 所以的子程序 结束之后,再彻底的去释放资源
这一机制 也 防止了 僵尸进程的出现
主进程 具有管理 子进程的功能