1.进程的其他方法
进程:正在进行的一个过程或者说一个任务.负责执行任务的是cpu,进程之间的空间是相互隔离的
使用multiprocess模块来开启进程
Process([group [, target[, name [, args [,kwargs]]]]])由该类实例化的对象,可用来开启一个子进程
开启进程:
from multiprocess import Process
p = Process(target=方法名)
参数介绍:
group未使用,值始终为None
target表示调用对象,即子进程要执行的任务
args表示调用对象的位置参数元组, args(1,)
kwargs表示调用对象的字典,kwargs = {'name':'bob'}
name为子进程的名称
方法:
p.start()开启进程
p.run()进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
p.terminate()强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法要小心这种情况.如果p还保存了一个锁那么也将不会释放,进而导致死锁
属性:
p.daemon守护进程
p.name进程的名称
p.pid 进程的pid 也使用 os.getpid来查看 os.getppid为查看父进程名字
示例:
import time
import os
from multiprocessing import Process
def f1():
print('子进程的pid',os.getpid())
print('子进程的父进程的pid',os.getppid())
print('aaa')
def f2():
print('bbb')
if __name__ == '__main__':
p1 = Process(target=f1,name='宝宝1')
p2 = Process(target=f2,)
p1.start()
p2.start()
print(p1.name)
print('子进程的pid',p1.pid)
print('父进程的id',os.getpid())
#进程的其他方法
def f1():
time.sleep(5)
print('子进程1号')
if __name__ == '__main__':
p = Process(target=f1,)
p.start()
print(p.is_alive()) #判断子进程是否还活着,是否还在运行
p.terminate() #给操作系统发送一个结束进程的信号
time.sleep(0.5)
print(p.is_alive())
验证进程之间的空间是相互隔离的:
from multiprocessing import Process
num = 100
def f1():
global num
num = 3
print('子进程中的num',num)
print('>>>>>',num)
if __name__ == '__main__':
p = Process(target=f1,)
p.start()
p.join()
print('主进程中的num',num)
2.守护进程
主进程结束,守护进程也跟着结束
p.daemon:
默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随即终止,并且设定为True之后,p不能创建字节的新进程,必须在p.start()之前设置
1.守护进程会在主进程代码执行结束后就终止
2.守护进程内再无法开启子进程,否则抛出异常:
AssertionError: daemonic processes are not allowed to have children
如果我们有两个任务需要并发执行,那么开一个主进程和一个子进程分别去执行就可以了,如果子进程的任务在主进程任务结束后就没有存在的必要了,那么该子进程应该在开启前就被设置成守护进程.主进程代码运行结束,守护进程随即终止
守护进程示例:
import time
from multiprocessing import Process
def f1():
time.sleep(3)
print('xxxx')
def f2():
time.sleep(5)
print('普通子进程的代码')
if __name__ == '__main__':
p = Process(target=f1,)
p.daemon = True #将该进程设置为守护进程,必须写在start之前,意思如果我的主进程代码运行结束了,你这个子进程不管运行到什么地方,都直接结束
p.start()
#开启一个普通的子进程来验证一下守护进程的结束只和主进程的代码运行结束有关系,而整个程序的结束需要主进程和普通的子进程的代码都运行结束才结束
p2 = Process(target=f2,)
p2.start()
#等待2号普通进程的结束,才继续执行下面主进程中的代码
# p2.join()
#守护进程会跟跟着父进程的代码运行结束,就结束
print('主进程结束')
3.进程锁/互斥锁
进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端没有问题.但共享带来竞争,进而导致错乱,这时就需要加锁处理.互斥锁的原理就是把病发改成串行,降低了效率,但保证了数据安全不错乱.
互斥锁与join()
join()将一个任务整体串行,互斥锁的好处就是可以将一个任务中的某一段代码串行.
loc = Lock()
#第一种方式
def func(loc):
loc.acquire()
需要锁的代码
loc.release()
#第二种方式
With loc:
需要锁的代码
4.进程间的数据共享
通过引入manager,结合Lock来实现进程之间的数据共享
1.使用for循环来创建子进程
import time
from multiprocessing import Process
def f1():
time.sleep(0.5)
print('xxx')
if __name__ == '__main__':
p_list = []
#for循环创建子进程,并且完成主进程等待所有子进程执行结束,才继续执行
for i in range(10):
p = Process(target=f1,)
p.start()
p_list.append(p)
p.join()
# for pp in p_list:
# pp.join()
print('主进程结束')
2.数据共享
为了保证进程间使用数据的安全,我们对数据处理时经常这样:
a = 10
tmp = a
tmp -= 1
a = tmp
a -= 1 # a = a - 1
示例:
def f1(m_d,l2):
# m_d['num'] -= 1 #
with l2:
# l2.acquire()
tmp = m_d['num']
tmp -= 1
time.sleep(0.1)
m_d['num'] = tmp
# l2.release()
if __name__ == '__main__':
m = Manager()
l2 = Lock()
m_d = m.dict({'num':100})
p_list = []
for i in range(10):
p = Process(target=f1,args=(m_d,l2))
p.start()
p_list.append(p)
[pp.join() for pp in p_list]
print(m_d['num'])
4.队列
进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种消息之间都是使用消息传递的
1.创建队列的类(底层就是管道和锁定的方式实现)
Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递
2.参数介绍:
maxsize是队列中允许的最大项数,省略则无大小限制
需要明确:
1.队列中存放的是消息而非大数据
2.队列占用的是内存空间,因而maxsize即便是无大小限制也受限于内存大小
3.主要方法介绍:
q.put()方法用来插入数据到队列中
q.get()方法可以从队列读取并且删除一个元素
q = Queue(5)
q.put() #满了会等待
q.get() #没有数据了会等待
q.qsize()
q.empty() 不可靠
q.full()不可靠
q.get_nowait() #不等待,但是报错
q.put_nowait() #不等待,但是报错
基于队列的进程间通信
from multiprocessing import Process,Queue
def f1(q):
q.put('约吗?')
if __name__ == '__main__':
q = Queue(3)
p = Process(target=f1,args=(q,))
p.start()
son_p_msg = q.get()
print('来自子进程的消息:',son_p_msg)
5.生产者消费者模型
1.程序中有两类角色
一类负责生产数据(生产者)
一类负责处理数据(消费者)
2.引入生产者消费者模型为了解决的问题是
平衡生产者与消费者之间的速度差
程序解开耦合
3.如何实现生产者消费者模型
生产者<--->队列<--->消费者
通过队列实现一个生产者<--->消费者模型,基于joinnamlequeue的在下面,这里只是呈现基本模型:
生产包子---> 吃包子
import time
from multiprocessing import Process,Queue
#生产者
def producer(q):
for i in range(10):
time.sleep(0.7)
s = '大包子%s号'%i
print(s+'新鲜出炉,拿去用')
q.put(s)
def consumer(q):
while 1:
time.sleep(1)
baozi = q.get()
print(baozi+'被吃了')
if __name__ == '__main__':
q = Queue(10)
pro_p = Process(target=producer,args=(q,))
con_p = Process(target=consumer,args=(q,))
pro_p.start()
con_p.start()
6.进程队列:JoinableQueue([maxsize])
这就像一个Queue对象,但队列允许项目的使用者通知生成者项目已经被成功处理.通知进程是使用共享的信号和条件变量来实现的
maxsize是队列中允许最大项数,省略则无大小限制
q.task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理.如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常
q.join():生产者调用此方法进行阻塞,直到队列中所有的项目均被处理.阻塞将持续到队列中的每个项目均
调用q.task_done()方法为止
基于JoinableQueue来实现生产者消费者模型:
import time
from multiprocessing import Process,Queue,JoinableQueue
#生产者
def producer(q):
for i in range(10):
time.sleep(0.2)
s = '大包子%s号'%i
print(s+'新鲜出炉,拿去用')
q.put(s)
q.join() #就等着task_done()信号的数量,和我put进去的数量相同时,才继续执行
print('所有的任务都被处理了,继续潜行吧骚年们')
def consumer(q):
while 1:
time.sleep(0.5)
baozi = q.get()
print(baozi+'被吃了')
q.task_done() #给队列发送一个取出的这个任务已经处理完毕的信号
if __name__ == '__main__':
# q = Queue(30)
q = JoinableQueue(30) #同样是一个长度为30的队列
pro_p = Process(target=producer,args=(q,))
con_p = Process(target=consumer,args=(q,))
pro_p.start()
con_p.daemon = True
con_p.start()
pro_p.join()
print('主进程结束')