8.2 进程

8.2.1 进程的创建

开启多进程scoketserver:server、client

进程的开启:python中的多线程,一定是有一个主进程,由主进程创建几个子进程,

Linux与Windows

相同点:都是由主进程创建子进程,主进程和子进程原则上都有相互隔离的独立空间,互不影响

不同点:Linux子进程空间的初始数据完全是从主进程中copy来的;Windows子进程空间数据也是从主进程copy但与主进程有所不同

进程的三种状态

阻塞状态:bolcked 进程等待的某种条件满足之前无法继续执行

运行状态:running 进程正在占用CPU资源

就绪状态:ready 进程以获得除处理器之外的所需资源,等待分配处理器资源

python多进程

Windows下使用Process模块开启多进程,一定要放在__main__下运行

第一种方式

from multiprocessing import Process
import time def task(name):
print(f"{name} is runing ")
time.sleep(3)
print(f"{name} is over ") if __name__ == '__main__': # windows环境下,开启多进程一定放在这个下面
p = Process(target=task,args=('盖伦',)) # args 一定是一个元组的形式.
p.start() # 通知操作系统,你给我在内存中开辟一个空间,将p这个进程放进去,然后让cpu执行,中间需要一定的时间开辟空间
time.sleep(1) print('hello')

第二种方法

from multiprocessing import Process
import time class MyProcess(Process): def __init__(self,name):
super().__init__() # 必须要继承父类的__init__
self.name = name def run(self): # 必须定义run名字.
print(f"{self.name} is runing ")
time.sleep(3)
print(f"{self.name} is over ") if __name__ == '__main__': # windows环境下,开启多进程一定放在这个下面
p = MyProcess('盖伦')
p.start()
print('main process')

8.2.2 进的id

当内存中存在多个进程时,如何识别进程的身份?

Windows终端中可以使用tasklist查看所有进程pid,使用tasklist | findstr 进程名可以查看单个具体的进程pid.

python中获取pid的方法:子进程pid--os.getpid();父进程pid--os.getppid()

import os

print(f'子进程:{os.getpid()}')
print(f'父进程:{os.getppid()}')

8.2.3 进程之间的数据隔离

原则上,个进程之间的数据是隔离的,因为创建子进程之后,会复制一份父进程的数据给子进程,下面进行验证:

from multiprocessing import Process
import time
import os x = 1000 def f():
global x
x = 2
print(f"子进程:{os.getpid()}") if __name__ == '__main__':
p = Process(target=f)
p.start()
time.sleep(2) # 让子进程先执行
print(x) # 输出 x = 1000,是数据隔离的
print(f"父进程:{os.getpid()}") # 注意:可以使用id(x)产看x在内存中的位置-5到255的 id(x)值相等,其他的都不相等

8.2.4 join**

主进程等待子进程结束之后再往下执行

用法:子进程.join()

两个实例感受一下

from multiprocessing import Process
import time def f(name,x):
print(f"{name} is runing")
time.sleep(x)
print(f"{name} is finishing") if __name__ == '__main__':
p1 = Process(target=f,args=('gailun',2))
p2 = Process(target=f, args=('zhaoxin', 1))
p3 = Process(target=f, args=('jiawen', 3))
start_time = time.time()
p1.start()
p2.start()
p3.start() p1.join()
p2.join()
p3.join() print(f"主进程运行时间{time.time()-start_time}")
# 最后输出在3.多秒
from multiprocessing import Process
import time def f(name,x):
print(f"{name} is runing")
time.sleep(x)
print(f"{name} is finishing") if __name__ == '__main__':
p1 = Process(target=f,args=('gailun',2))
p2 = Process(target=f, args=('zhaoxin', 1))
p3 = Process(target=f, args=('jiawen', 3))
start_time = time.time() p1.start()
p1.join() p2.start()
p2.join() p3.start()
p3.join() print(f"主进程运行时间{time.time()-start_time}")
# 输出在6.多秒

8.2.5 进程对象的其他属性**

terminate() 结束子进程与start一样只是告诉系统要终止这个进程;内存中不管是终止或是开启子进程,都是耗费时间的,命名执行后系统不一定会立马运行

is_alive() 判断子进程是否存活

8.2.6 僵尸进程与孤儿进程**

只有Linux(Mac)环境下才强调的两个概念,Windows下没有

僵尸进程

​ 一般来说,主进程是子进程的发起者,父子进程的运行是异步的,主进程往往会调用wait或者waitpid获取子进程的状态信息;如果子进程结束了而父进程没有获取子进程的状态信息,那么子进程的进程描述就会一直保留在系统中,这时子进程就成为僵尸进程。

​ 子进程没有可执行代码后将变成僵尸进程,如果父进程一直运行,又没有处理僵尸进程的代码,僵尸进程也将一直存在,消耗资源。僵尸进程无法通过kill命令杀掉,因为僵尸进程是已经停止的,所以使用杀死进程的方法来杀僵尸进程是无效的。僵尸进程不使用CPU或硬盘等系统资源,而只使用极少量的内存用于存储退出状态和资源使用信息。

【危害】僵尸进程在系统中保存的信息一直不释放的话,会一直占用内存和进程号,如果大量的进程号被占用之后,系统就不能产生新的进程,因为系统分配的进程号时有限的。

孤儿进程

如果主进程由于各种原因,提前退出,但是他的子进程还在运行,这时把它所有的子进程称为孤儿进程,一段时间之后,init进程会对孤儿进程进行回收。

孤儿进程是无害的,init会来回收他

8.2.7 守护进程

子进程对父进程进行守护

语法:位置放在start之前

进程名.daemon = True
进程名.start()
  • 守护进程会在主进程代码执行结束后就终止
  • 守护进程内不能再开启新的子进程,否则抛出异常AssertionError
from multiprocessing import Process
import time def f(name):
print(f"{name} is piaoing" )
time.sleep(3)
print(f"{name} is piao end") if __name__ == '__main__':
p = Process(target=f,args=('gailun',))
p.daemon = True
p.start()
time.sleep(2)
print('this is main_process')
# 输出
gailun is piaoing
this is main_process
from multiprocessing import Process
import time def f1(name):
print(f"{name} is runing")
time.sleep(2)
print(f"{name} is end") def f(name):
print(f"{name} is piaoing" )
p2 = Process(target=f1, args=('zhaoxin',))
p2.start()
time.sleep(3)
print(f"{name} is piao end")
# 报错:AssertionError: daemonic processes are not allowed to have children

8.3 互斥锁

​ 因为进程之间数据是不共享的,但却公用同一个操作系统,某些情况下多个进程会公用某一个资源,例如打印机,下面来进行说明

​ 【情况一】

from multiprocessing import Process
import time
import random def task1():
print('task1 start print')
time.sleep(random.randint(1,3))
print('task1 done') def task2():
print('task2 start print')
time.sleep(random.randint(1, 3))
print('task2 done') def task3():
print('task3 start print')
time.sleep(random.randint(1, 3))
print('task3 done') if __name__ == '__main__':
p1 = Process(target=task1)
p2 = Process(target=task2)
p3 = Process(target=task3) p1.start()
p2.start()
p3.start()

这种情况的虽然有效率,但是打印的顺数是乱的,我们希望的是,一个进程打印完了,下一个进程才能打印

​ 【情况二】

p1.start()
p1.join()
p2.start()
p2.join()
p3.start()
p3.join()

这种情况虽然实现了依次打印问题,但是顺序是我们设定好的,没有遵循公平竞争资源的原则

​ 【情况三】

from multiprocessing import Process,Lock
import time
import random def task1(lock):
print('---task1---') # 验证CPU遇到io是否切换
lock.acquire()
print('task1 start print')
time.sleep(random.randint(1,3))
print('task1 done')
lock.release() def task2(lock):
print('---task2---') # 验证CPU遇到io是否切换
lock.acquire()
print('task2 start print')
time.sleep(random.randint(1, 3))
print('task2 done')
lock.release() def task3():
print('---task3---') # 验证CPU遇到io是否切换
# lock.acquire()
print('task3 start print')
time.sleep(random.randint(1, 3))
print('task3 done')
# lock.release() if __name__ == '__main__':
lock = Lock()
p1 = Process(target=task1,args=(lock,)) # 以参数的形式传进进程中
p2 = Process(target=task2,args=(lock,))
p3 = Process(target=task3) p1.start()
p2.start()
p3.start()

【总结】

  • 使用Lock类的acquire()和release()函数,加锁可以保证多个进程竞争同一个资源时,同一时间只能由一个进程可以进行修改,虽然把多个进程变成串行,但是保证了数据的安全性。
  • 一个acquire必须跟一个release,两个连续的acquire会造成死锁,死锁的常常发生在带锁的进程未解锁之前又生成了带锁的进程
  • 从第三个实列来看,CPU遇到IO时还是切换了进程,只不过碰见带锁的代码时会跳过,去执行不带锁的代码

8.4 进程间通信

进程与进程间是互相隔离的。如果要实现两个进程间的通信,可以借助硬盘里的同一文件,但是该方法着实有些效率低,所以我们选择使用队列或者管道,但是管道又因为bug无法保证数据安全性和稳定性等等被诟病,往往选择队列。

【使用文件模拟抢票】
# 需求:1.买票之前,先经过查票阶段,查票是并行发生
# 2.买票,从服务端获取剩余票数,买票,票数减一,中间有网络延迟
# 数据隔离,只是内存层面的隔离,不代表硬盘上的数据也隔离 from multiprocessing import Process,Lock
import time
import random
import os
import json def serch():
with open('db.json',mode='r',encoding='utf-8') as f1 :
dic = json.load(f1)
print(f'剩余票数:{dic["count"]}') def get():
with open('db.json',mode='r',encoding='utf-8') as f1:
dic = json.load(f1) time.sleep(random.random()) # 模拟网络延迟
if dic["count"] > 0 :
dic["count"] -= 1
with open('db.json', mode='w', encoding='utf-8') as f2:
json.dump(dic,f2)
print(f"{os.getpid()}购票成功")
else:
print('该班列车票已售完') def task(l):
serch()
l.acquire()
get()
l.release() if __name__ == '__main__':
with open('db.json', mode='w', encoding='utf-8') as f:
json.dump({'count':3},f) lock = Lock()
for i in range(5):
p = Process(target=task,args=(lock,))
p.start()

8.4.1 队列Queue

队列是存在于内存中的容器

队列的特点:先进先出(FIFO)原则

【常用方法】
  • maxsize() q = Queue(3) 数据量不易过大.精简的重要的数据
  • .put(对象) -- 往队列中插入数据,其中有两个可选参数blocked和timeout。blocked默认为True,如果put的对象数量超过maxsize,则会阻塞进程,blocked默认为False时,直接报Queue.Full异常;timeout参数设定之后,是在timeout时间之后如果队列还无法插入数据载报Queue.Full异常
  • .get(对象) -- 从队列中拿取一个元素,get函数也有两个参数blocked和timeout。blocked默认为True,如果队列为空时,继续get就会阻塞进程,这是如果尤其的进程王该队列中添加了数据,阻塞的进程还是可以取到数据,blocked为false且队列为空时,直接报Queue.Empty异常,timeout参数设定之后,是在timeout时间之后如果队列还无法插入数据载报Queue.Empty异常
  • q.get_nowait():同q.get(False)
  • q.put_nowait():同q.put(False)
  • q.empty():调用此方法的此时此刻q为空则返回True,如果队列中又加入了项目,它不会在意,所以该函数返回的结果是不可靠的
  • q.full():调用此方法的此时此刻q已满则返回True,该结果也是不可靠的,比如在返回True的过程中,队列中的项目被取走
  • q.qsize():返回队列中此时此刻项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样
from multiprocessing import Queue
from multiprocessing import Process
import os def task(q):
try :
q.put(f"{os.getpid()}",block=False)
except Exception:
return if __name__ == '__main__':
q = Queue(10)
for i in range(12):
p = Process(target=task,args=(q,))
p.start()
for i in range(1,11):
print(f"排名第{i}的用户:{q.get()}")

8.4.2 生产者消费者模型

【形成的基本条件条件】两个进程 (生产者、消费者) + 缓冲区(队列);

生产者负责产生数据,消费者负责处理数据,队列缓冲区负责连接两个进程间通信的临界资源

【问题的核心】生产者在队列满后要暂时停止存入数据,消费者在队列空时,暂时不要取数据

【用于何处】多用于解决并发问题,处理生产数据与处理数据的进程间的强耦合问题,平衡二者的工作速率,从而提高整体的效率

# 生产者:生成数据
# 消费者:处理数据
# 缓冲区
# 用于并发 from multiprocessing import Process,Queue
import random
import time
import os def producer(q):
for i in range(1,10):
time.sleep(random.randint(1,2))
res = f"{i}号武器"
q.put(res)
print(f"\033[0;32m 邦德锻造了{res} \033[0m") def consumer(q):
while 1:
try: # 当生产者不再生产,队列为空时要停止消费者继续取值
time.sleep(random.randint(1,2))
ret = q.get(timeout=4)
print(f"\033[0;33m 士兵拿走了{ret} \033[0m")
except Exception:
return if __name__ == '__main__':
q = Queue()
p = Process(target=producer,args=(q,))
c = Process(target=consumer,args=(q,)) p.start()
c.start()

对于临界,除了以上try的处理方式外,还可以在生产者进程内put一个停止取值的信号,当消费者在队列中取到该值时,就会停止继续取值;还可以在主程序下,配合join向队列中put 停止信号,这种方式相对low一些,因为有多少个生产者就要写多少个停止信号

Python学习之进程的更多相关文章

  1. Python学习--17 进程和线程

    线程是最小的执行单元,而进程由至少一个线程组成.如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间. 进程 fork调用 通过fork()系统调用,就可以生成一个子进程 ...

  2. Python学习--18 进程和线程

    线程是最小的执行单元,而进程由至少一个线程组成.如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间. 进程 fork调用 通过fork()系统调用,就可以生成一个子进程 ...

  3. python学习笔记-进程线程

    1.什么是进程(process)? 程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程.程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述 ...

  4. python学习(十三)进程和线程

    python多进程 from multiprocessing import Process import os def processFunc(name): print("child pro ...

  5. python 学习分享-进程

    python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程.Python提供了非常好用的多进程包multiprocessing,只需要定 ...

  6. Python学习-day10 进程

    学习完线程,学习进程 进程和线程的语法有很多一样的地方,不过在操作系统中的差别确实很大. 模块是threading 和 multiprocessing 多进程multiprocessing multi ...

  7. python学习之-- 进程 和 线程

    python 进程/线程详解 进程定义:以一个整体的形式暴露给操作系统管理,它里面包含对各种资源的调用,内存的管理,网络接口的调用等等,对各种资源管理的集合,就可以叫做一个进程. 线程定义:线程是操作 ...

  8. day34 python学习 守护进程,线程,互斥锁,信号量,生产者消费者模型,

    六 守护线程 无论是进程还是线程,都遵循:守护xxx会等待主xxx运行完毕后被销毁 需要强调的是:运行完毕并非终止运行 #1.对主进程来说,运行完毕指的是主进程代码运行完毕 #2.对主线程来说,运行完 ...

  9. Python学习---线程/协程/进程学习 1220【all】

    Python学习---线程基础学习 Python学习---线程锁/信号量/条件变量同步1221 Python学习---同步条件event/队列queue1223 Python学习---进程 1225 ...

随机推荐

  1. BZOJ1787 [Ahoi2008]Meet 紧急集合[结论题]

    location. 求到树上三点距离和最短的点及此距离. 这个不还是分类讨论题么,分两类大情况,如下图. 于是乎发现三个点对的lca中较深的那个lca是答案点.距离就是两两点对距离加起来除以2即可.这 ...

  2. 严重: Servlet [SelectController] in web application [/servlet4] threw load() exception

    在web.xml路径配置.jar包导入都正确的情况下,那就考虑是环境问题. 1.servers-->clean 将代码从tomcat中清除 2.Project-->clean  将ecli ...

  3. 1,Java消息服务-JMS

    一,消息服务 消息服务指的是两个应用程序之间进行异步通信的API,它为标准消息协议和消息服务提供了一组通用接口,包括创建.发送.读取消息等,用于支持应用程序开发.在Java中,当两个应用程序使用JMS ...

  4. js 两个页面的传值 可以用父页面 子页面做

    js  两个页面的传值  可以用父页面 子页面做 比如弹窗  将值传到子页面的时候  用get超长

  5. List集合的三个实现类比较

    1. ArrayList 底层数据结构是数组,查询快,增删慢 线程不安全,效率高 2. Vector 底层数据结构是数组,查询快,增删慢 线程安全,效率低 3. LinkedList 底层数据结构是链 ...

  6. Java多线程和并发(十一),CAS(Compare and Swap)

    目录 1.CAS简介 2.CAS多数情况下对开发者来说是透明的 3.CAS缺点 十一.CAS(Compare and Swap) Synchronized直观意义上是一种悲观锁 cas则是乐观锁的一种 ...

  7. 在百度ueditor上粘贴从word中copy的图片和文字 图片无法显示的问题

    我这边从world 里面复制粘贴图片到编辑器中,它自动给我上传了,但是我是用的第三方的要设置一个token值,我找了很久,也没有找到应该在哪里设置这个上传的参数,如果是点击图片上传,我知道在dialo ...

  8. codevs 2602 最短路径问题x

                         题目描述 Description 平面上有n个点(n<=100),每个点的坐标均在-10000~10000之间.其中的一些点之间有连线.若有连线,则表示 ...

  9. Spring Boot教程(二十七)整合Spring Security

    在这一节,我们将对/hello页面进行权限控制,必须是授权用户才能访问.当没有权限的用户访问后,跳转到登录页面. 添加依赖 在pom.xml中添加如下配置,引入对Spring Security的依赖. ...

  10. codefroces Round #201.a--Difference Row

    Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Description You wa ...