本文内容:创建进程的两种方式、进程与进程之间的数据隔离、僵尸进程和孤儿进程、守护进程、进程锁
进程的初始:
程序:应用软件,一堆代码文件
进程:一个正在执行的程序/文件,抽象的概念
启动一个进程的具体过程:程序存出硬盘中,当双击exe,文件会率先触发操作系统,操作系统就会将你的程序加载到内存,然后交于cpu进行处理
进程的由来:源于操作系统
第三代计算机实现了:集成电路,多道程序技术
多道技术:
空间上的复用(内存可以加载很多个不同的任务)
时间上的复用:
1.遇到进程中的IO时,cpu就切换。 IO(指的是 输入到内存,以及从内存中输出的两种状态)
2.一个进程长时间被cpu处理时,操作系统就会强硬的将cpu调出去去处理下一个进程
串行:cpu将一些进程一个接一个按顺序的执行
并发:一个cpu执行多个进程,并不是等某个进程执行完才去执行下一个进程,而是当该进程遇到IO操作时或者该进程执行时间过长,cpu就会去执行其他的进程。 这样看起来像是同时执行,但并不是,不过极大的缩减了时间,提高了效率
并行:(和cpu的数量有关)4个cpu可以同时处理四个进程。 真正意义上的 一对一服务 效率也是最高
开启子进程必须保证两点:
1.对原数据的保存
2.对原状态的保存
主进程代码不会等子进程结束才执行
如果使用 Process创建子进程,主进程会等子进程执行完。但是如果使用进程池创建的化, 主进程是不会等子进程的。主进程代码执行完毕整个程序执行完毕了
在Python代码中关于进程的代码操作:
1 创建进程的两种方式:
1)注意windows环境下想开启子进程一定要 if __name__ == __main__ 下:
from multiprocessing import Process
def task(name):
print("%s开启啦"%name)
if __name__ == "__main__":
p = Process(target=task, args= ("admin",))
# p = Process(target=task, args= {"name":"admin"}) 和上面二选一 都可以
p.start()
print("主进程依旧在进行")
2)利用框架去构建进程
from multiprocessing import Process
class MyProcess(Process):
def __init__(self, name):
super().__init__() # 此处必须调用父类的 __init__ 方法
self.name = name
def run(self):
print("这里面写子进程的代码。 只能说 start 和 run 有关系,但是还为了解到具体的关系")
if __name__ == "__main__":
p = MyProcess("admin")
p.start()
print("主进程依旧在运行")
2.查看进行的pid,以及该进程的父进程的pid
1) 在cmd终端查看
windows:
tasklist 终端查看 所有进程 的 信息
tasklist | findstr python 终端查看 指定进程 的 信息
2) 利用 os 模块
import os
from multiprocessing import Process
def task():
print(os.getpid()) # 查看该进程自己的 pid
print(os.getppid()) # 查看该进程的父进程的 pid
if __name__ == "__main__":
p = Process(target=task)
p.start()
3)利用 进程 自己打印 本身的pid
from multiprocessing import Process
def task():pass
if __name__ == "__main__":
p = Process(target=task)
p.start()
print(p.pid) # 打印该进程的pid
3.进程与进程之间的数据隔离:
进程与进程之间本身是空间隔离的,也就是 子进程内的数据修改不会影响到父进程的数据,不过可以通过 方法 使其不隔离。
如:(生产者与消费者模式)
进程之间通信 IPC
from multiprocessing import Process, Queue
def prodece(q):
q.put("我放了一个数据")
def consumer(q):
print(q.get())
if __name__ == "__main__":
q = Queue() # Queue 其实是一个队列,特点就是先进先出
p = Process(target=prodece,args=(q,)) # 向队列中放入数据
p1 = Process(target=consumer, args = (q,)) # 从队列中取出数据
p.start()
p1.start()
生产者和消费者模式:
import random
import time
from multiprocessing import Process, Queue
# 生产者
def prodece(name,q):
for i in range(1,11):
time.sleep(random.uniform(1,2)) # 模拟生产者的生产速度,生产者要慢于消费者
q.put("菠萝%s号"%(i))
print("———— 菠萝%s号生产完毕 ————"%(i))
# 消费者
def consumer(name,q):
while 1: # 消费者一直取
task = q.get() # 无论有无 都在等着
if not task: # 如果 生产者自己传输一个 None, 则证明生产完毕
break
time.sleep(random.random()) # 模拟消费者的消费速度,但要远大于生产者的速度
print("%s被%s吃了"%(task,name))
if __name__ == "__main__":
q = Queue() # 创建一个队列
pro_names = ["富士康","华硕","小米"]
pro_li = []
for pro_name in pro_names: # 循环建立生产者
pro_p = Process(target=prodece, args=(pro_name,q))
pro_p.start()
pro_li.append(pro_p) # 将建立的生产者进程 添加到一个列表中
con_names = ["马爸爸","校长"]
con_li = []
for con_name in con_names:
con_p = Process(target=consumer, args=(con_name,q))
con_p.start()
con_li.append(con_p) # 将建立的消费者进程 添加到一个列表中
for p in pro_li:
p.join() # 使 生产者全部都执行完毕后
for p in con_li:
q.put(None) # 有几个 消费者,就需要再向 对列中 放几个 None,否则,第一个执行完None,后就 break,其他的 消费者进程还没有 执行结束,就被迫 停止循环了
4.进程的属性
print(p.name) #Process-1 进程的名字
print(p.daemon) # False 守护进程 p.daemon = True 就会设置 p 为 主进程 的守护进程
print(p.ident) # 16716 进程的pid
print(p.pid) # 16716 进程的pid
print(p.terminate()) # None 杀死进程,类似于 start() 方法,也是向操作系统发出指令,但是不会立马杀死,会有时间差
print(p.is_alive()) # True 进程的存活状态
额外注意 :
p.json() # 告诉 父进程:等我子进程执行完后,才能执行父进程
当p.json() 连续的时候 是相对同时的向操作系统发出指令,
但如果 p.json() 不连续的时候,是分开向操作系统发出指令的
1)连续的时候:
from multiprocessing import Process
import time
def task():
time.sleep(3)
if __name__ == '__main__':
star = time.time()
p = Process(target=task)
p1 = Process(target=task)
p2 = Process(target=task)
p.start()
p1.start()
p2.start()
p.join()
p1.join()
p2.join()
print(time.time()-star) #3.1323935985565186
2)不连续的时候:
# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
def task():
time.sleep(3)
if __name__ == '__main__':
star = time.time()
p = Process(target=task)
p1 = Process(target=task)
p2 = Process(target=task)
p.start()
p.join()
p1.start()
p1.join()
p2.start()
p2.join()
print(time.time()-star) #9.355775833129883
5.僵尸进程和孤儿进程
僵尸进程:子进程结束了,但是父进程没有对其进行回收 孤儿进程:子进程没结束,但是父进程异常退出 一:僵尸进程(有害) 僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。详解如下 我们知道在unix/linux中,正常情况下子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束,如果子进程一结束就立刻回收其全部资源,那么在父进程内将无法获取子进程的状态信息。 因此,UNⅨ提供了一种机制可以保证父进程可以在任意时刻获取子进程结束时的状态信息: 1、在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等) 2、直到父进程通过wait / waitpid来取时才释放. 但这样就导致了问题,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。 任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。 二:孤儿进程(无害) 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。 孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。
6.守护进程
# 守护进程会等待主进程的代码结束之后就立即结束
# 守护进程也是一个子进程,主进程永远要在子进程结束之后才能结束,因为主进程要负责回收子进程的资源
from multiprocessing import Process
def task():
print("子程序运行")
if __name__ == '__main__':
p = Process(target=task)
p.daemon = True #设置 p 这个子程序 为 主程序的 守护进程,并且 这句话必须放在 p.start() 的上方
p.start()
print("主程序运行")
"""
———— 打印结果 ————
主程序运行
"""
7,进程锁
例如在买票的过程中,需要对 买票这个环节 进行加锁,也就是 只允许一个进程去执行该操作,虽然是失去了效率,但保护了数据安全
import time
from multiprocessing import Process,Lock
def select_ticket(name):
time.sleep(0.05)
print("%s查票"%(name))
def buy_ticket(name):
time.sleep(0.1)
print("%s买票"%(name))
def user_process(lock, name):
select_ticket(name)
with lock: # 此处加锁,只允许一个 进程 去执行该方法
buy_ticket(name)
if __name__ == '__main__':
lock = Lock()
for i in range(3):
p = Process(target=user_process, args = (lock,i))
p.start()
print("主程序运行")
注意:
with lock:
buy()
等同于:
lock.acquire()
buy()
lock.release()