一:进程理论
1.程序和进程
程序就是一堆代码文件,是指令和数据的集合,可以作为目标文件保存在磁盘中,或者作为段存放在内存地址空间中。(静态)
进程就是一个程序运行的过程,是操作系统进行资源分配和保护的基本单位(动态)
进程是资源分配的最小单位
1个程序可以对应多个进程,但是1个进程只能对应1个程序。进程和程序的关系犹如演出和剧本的关系
2.并发、并行、串行
**串行:**多个任务依次运行、一个进程运行完毕、在运行下一个
**并发:**多个任务看起来像是在同时运行,本质上还是一个个地运行
**并行:**多个任务真正意义上一起运行,只有多个CPU才能并行的概念
3.同步-异步/阻塞 - 非阻塞
同步(慢):
发起一个请求,直到请求返回结果之后,才进行下一步操作
就行运行在一段代码,自上而下一行一行运行,只有上一行代码执行完成,才能执行下一行代码
简单来说:同步就是:必须一件事一件事的做,等前一件事做完了,才能做下一件事
异步(快):
发起一个请求后,不管这个请求有没有返回结果,直接可以进行下一步操作
一般情况下,有一个回调的操作
简单来说:异步就是:可以多件事情同时做。
阻塞(慢):
遇到IO操作,CPU会被操作系统切换到其它进程。
调用结果返回之前,当前线程会被挂起。调用线程只用在得到结果之后才会返回。
简单来说,阻塞就是:需要买奶茶和冰淇淋,买奶茶的时候奶茶制作过程只能等,然后买完奶茶再去买冰淇淋
非阻塞(快):
没有IO操作,一直运行
在不能立刻得到结果之前,改调用不会阻塞当前线程
简单来说,非阻塞就是:需要买奶茶和冰淇淋,买奶茶的时候等的时间去买冰淇淋。
最佳状态:异步+非阻塞
拓展:
同步框架:Flak、Django3.0之前
异步框架:Tornado、Sanic、FastAPI
例子:
背景:妈妈出门了,小明想要看电视,但是妈妈让小明烧水。
1.小明烧水,在一旁等待,时不时看一下水有没有烧开。 —— 同步阻塞
2.小明烧水,等待的时间偷偷去看会儿电视,看电视的时候时不时来看一下水有没有烧开。 —— 同步非阻塞
3.小明买了一个水开了之后会响的水壶来烧水,烧水时在一旁等着,不用时不时看水有木有烧开。 —— 异步阻塞
4.小明用那个会响的水壶烧水,然后去看电视,等水开了之后发出响声,再去开。 —— 异步非阻塞
4.进程运行的三种状态
打开一个应用程序,该进程会进入就绪态,获取CPU资源后,执行程序代码,进入运行态,出现了读写文件with open(…),就会进入阻塞态,CPU资源就会被操作系统分配到其他进程
运行态(Running)(非阻塞):进程已获CPU,正在执行。单处理机系统中。处于执行状态的进程只有一个;多处理机系统中,有多个处于执行状态的进程。
就绪态(Ready)(非阻塞):进程以获得除CPU外的所有必要资源,只等待CPU时的状态。一个系统会将多个处于就绪状态的进程排成一个就绪队列
阻塞态(Blocked)(阻塞):正在执行的进程由于某种原因(**文件读写等IO操作)而暂时**无法继续执行,便放弃处理机而处于暂停状态,即进程执行受阻。(这种状态又称等待状态或封锁状态)
就绪 → 运行
处于就绪状态的进程,当进程调度程序为之分配了处理器后,该进程便由就绪状态转变成运行状态。
运行 → 就绪
处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从运行状态转变成就绪状态。
运行 → 阻塞
正在执行的进程因等待某种事件(文件读写等IO操作)发生而无法继续执行时,便从运行状态变成阻塞状态。
阻塞 → 就绪
处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。
优化程序效率的核心法则
降低IO操作(硬盘IO、网络IO)
数据获取优先级:
内存 > 本地硬盘 > 网络IO
5.提交任务的2种方式
同步调用:一个任务必须要执行完毕才能执行另一个任务
异步调用:一个任务在执行过程中,可以执行另一个任务
二:进程的使用
### 1.进程的创建
Windows:CreateProcess
Linux:Fork
2.进程的终止
1.正常退出(自愿;程序执行完毕后,终止,资源被回收)
2.出错退出(自愿;python3 test.py
但是test.py
不存在)
3.严重错误(非自愿;执行非法指令)
4.被其他进程杀死(非自愿;被操作系统杀死taskkill /F /PID 3333
)
三:join方法
0.作死法
import time
from multiprocessing import Process
def task(n):
print('%s run' % n)
time.sleep(10000)
if __name__ == '__main__':
for i in range(10000):
p = Process(target=task, args=(1,))
p.start()
print('主进程')
1.单个进程 + 单个子进程
import os
import time
from multiprocessing import Process
def task(n):
print(f'task[{os.getpid()}] is running')
time.sleep(n)
print(f'task[{os.getpid()}] is done')
if __name__ == '__main__':
# p = Process(target=task, args=(5,)) # args中为元组/列表
p = Process(target=task, kwargs={
'n': 5}) # kwargs中为字典
p.start() # 在向发操作系统发送启动子进程的信号,属于IO操作,速度慢
print(f'主进程[{os.getpid()}]')
# 主进程[15188]
# task[86956] is running
# task[86956] is done
2.单个父进程 + 多个子进程 方式1
import os
import time
from multiprocessing import Process
def task(n, name):
print(f'task[{name}] is running [{os.getpid()}]')
time.sleep(n)
print(f'task[{name}] is done [{os.getpid()}]')
if __name__ == '__main__':
p1 = Process(target=task, args=(2, 'p1'))
p2 = Process(target=task, args=(2, 'p2'))
p3 = Process(target=task, args=(2, 'p3'))
p1.start() # 在向发操作系统发送启动子进程的信号,属于IO操作,速度慢
p2.start() # 在向发操作系统发送启动子进程的信号,属于IO操作,速度慢
p3.start() # 在向发操作系统发送启动子进程的信号,属于IO操作,速度慢
print(f'主进程[{os.getpid()}]')
# 主进程[79800]
# task[p1] is running [17320]
# task[p3] is running [66276]
# task[p2] is running [36784]
# task[p1] is done [17320]
# task[p3] is done [66276]
# task[p2] is done [36784]
3.单个父进程 + 多个子进程 方式2
import os
import time
from multiprocessing import Process
class Myprocess(Process):
def __init__(self, tag):
super().__init__()
self.tag = tag
def run(self) -> None: # 表示run函数的返回值为空,无返回值
print(f'task[{self.tag}] is running [{os.getpid()}]')
time.sleep(3)
print(f'task[{self.tag}] is done [{os.getpid()}]')
if __name__ == '__main__':
p1 = Myprocess('进程1')
p2 = Myprocess('进程2')
p3 = Myprocess('进程3')
p1.start() # 相当于:p1.run,但是p1.run是同步的,所以这里用p1.start
p2.start() # p2.run
p3.start() # p3.run
print(f'主进程[{os.getpid()}]')
4.进程之间的数据是隔离的
因为:开启1个进程,就是开启了1个Python解释器。
Windows下要开进程,需要在main函数下
import os
import time
from multiprocessing import Process
age = 18
def task(n, name):
global age # 局部修改全局
age = 99
print(f'task[{name}] is running [{os.getpid()}]')
time.sleep(n)
print(f'task[{name}] is done [{os.getpid()}]')
print(f'子进程的age:{age}')
if __name__ == '__main__':
p1 = Process(target=task, args=(1, 'p1'))
p1.start()
print(f'主进程[{os.getpid()}]')
print(f'主进程的age:{age}')
# 主进程[2680]
# 主进程的age:18
# task[p1] is running [45116]
# task[p1] is done [45116]
# 子进程的age:99
5.join - 让子进程的开启者 等p1开启并运行完毕后 再运行
import os
import time
from multiprocessing import Process
def task(n, name):
print(f'task[{name}] is running [{os.getpid()}]')
time.sleep(n)