Python——multiprocessing(进程模块)
注意点:
1. 各操作系统中创建进程的方法不同。运行方式也有所不同。
windows创建进程:
执行开启进程的代码
1. Process实例化后,.start()来创建一个进程,并告诉系统,创建完进程后请执行Process内的target所指定的函数。
2. 由于是进程之间数据无法交互的问题,那么windows会以import的方式来将父进程的代码导入到子进程中,并运行。
3. 如果此时没有__name__判断语句,那么将会继续执行Process语句进行子进程的创建。反复创建直到报错。
IOS Linux操作系统创建进程:
是使用fork来将父进程代码运算后得出的值进行直接的拷贝(将内存地址直接拷贝),而不是在子进程中再运行一遍。
使用multiprocessing模块来创建一个进程。
from multiprocessing import Process
import time
import os
def func():
print('A',os.getpid())
time.sleep(1)
print('B',os.getpid())
if __name__ == '__main__': #windows中必须要写,如果不写将会报错
P = Process(target=func) #准备创建一个进程,并将func函数加入其中
P.start() #将func函数丢给系统,让系统自行创建一个进程,并运行其中的代码。
print('C',os.getpid()) #自己将继续往下执行自己进程中的事情。
exit()
print('D', os.getpid())
'''
C 17344
A 9400
B 9400
在P.start时,程序会将func代码直接给系统,让系统创建一个进程然后执行func中的代码。而不去管执行的怎么样,自己则继续往下执行。
因为CD中间有一个退出语句,那么就代表直接关闭该进程,而导致不会继续该进程下的D输出。
'''
方法和功能:
传参给要运行的函数。
Process(target=func,args=(a,))
必须是元组形式,如果传一个参数,加逗号,
from multiprocessing import Process
import os
def func(a,):
print(a,os.getpid())
if __name__ == '__main__':
P = Process(target=func,args=(1,))
P.start()
daemon= True
将子进程更改为守护进程,特点是随着主进程的代码结束而结束。如果有守护进程或非守护进程,那么当主进程结束时,先结束守护进程,然后等待其他子进程结束后回收资源并关闭。
def func1():
print('这是func1')
time.sleep(0.5)
if __name__ == '__main__':
P1 = Process(target=func1)
P1.start()
P1.daemon = True
print('结束')
is_alive()
判断子进程是否开启,开启显示True,未开启显示False,如果结束子进程后立即查看子进程状态,那么还是会有可能出现True,因为系统还没来得及进行关闭进程。
def func1():
print('这是func1')
time.sleep(0.5)
if __name__ == '__main__':
P1 = Process(target=func1)
P1.start()
print(P1.is_alive())
print('结束')
join()
将异步非阻塞更改为阻塞状态,等待所有的子进程结束后,再将主进程向下执行。典型的同步阻塞状态。
from multiprocessing import Process
import time
# import join
import os
def func(a,):
print(a,os.getpid())
if __name__ == '__main__':
P = Process(target=func,args=(1,))
P.start()
P.join()
print(2)
terminate()
结束进程,异步非阻塞状态。发送给计算机后,会继续执行后面的命令。
def func1():
print('这是func1')
time.sleep(0.5)
if __name__ == '__main__':
P1 = Process(target=func1)
P1.start()
P1.terminate()
print(P1.is_alive())
print('结束')
在面向对象中创建进程:
class mult_process1(Process): # Process必须继承
def __init__(self,a,b): #传参必须要继承一下Process中的init方法,然后再进行传参。
super().__init__()
self.a = a
self.b = b
def run(self): # run必须要写, start开启一个子进程,在子进程中调用run方法。(源码)
print('这是run1')
time.sleep(0.5)
class mult_process2(Process): # Process必须继承
def run(self): # run必须要写, start开启一个子进程,在子进程中调用run方法。(源码)
print('这是run2')
time.sleep(0.5)
if __name__ == '__main__':
p1 = mult_process1(1,2) #传参方式。
p1.start()
p2 = mult_process2()
p2.start()
锁,Lock
当使用多进程或多并发的时候,需要处理同一个文件时,为了确保文件的正确和安全。当给一段代码加锁后,只允许一个用户进行文件操作,其他用户只能排队等待。
加锁的场景:
1. 操作共享的数据资源(文件,数据库)
2. 对资源进行修改。
3. 保证数据安全或不考虑有影响代码速率。
from multiprocessing import Process,Lock
import json
import time
def run(i,lock):
with lock: #将整段代码进行加锁。相当于:头部加:lock.acquire() ,尾部加:lock.release()
time.sleep(0.2)
with open('log.log','r') as f:
f = int(f.read())
print(f'这是run{i},取了{f}')
f-=1
if f >= 0 :
time.sleep(0.2)
with open('log.log','w') as c:
json.dump(f,c)
if __name__ == '__main__':
lock = Lock() #实例化Lock
for i in range(10):
p1 = Process(target=run,args=(i,lock)) #将实例化后的lock传入子进程中。
p1.start()
进程之间的通信,Queue
当主进程需要让子进程返回数据,并不占用主进程的向下业务时,就需要让子进程运行完成后,返回一个值。
def run(i,q):
q.put(i+1) #put上传值
if __name__ == '__main__':
q = Queue() #实例化
p1 = Process(target=run,args=(8,q)) #传入子进程中
p1.start()
print(q.get()) #获取值
注意点:
1. get(获取)或put(上传),会导致队列空或者队列满时(5)会发生阻塞情况。
2. 使用put_nowait和get_nowait不会阻塞,但超出或者空时会报错,可以使用try抓取报错来解决。
3. 队列使用socket(文件操作类)、pickle、lock来同时完成队列操作。相比于pipo(管道),要多了一个lock,更加的安全。
4. 使用nowait时会存在数据丢失现象,能不用尽量不使用。
from multiprocessing import Process,Queue
import queue #是抓取异常来使用的
def run(i,q):
q.put_nowait(i+1)
if __name__ == '__main__':
q = Queue()
p1 = Process(target=run,args=(8,q))
p1.start()
try:
print(q.get_nowait())
except queue.Empty: #异常的报错是Empty和Full
pass
实例:
生产者消费者模型:
把一个生产数据,并且处理数据的过程进行解耦,让生产数据的过程和处理数据的过程达到一个工作效率上的平衡。将大程序分解成多个小功能进行处理。并使用队列来对内存队列进行控制。
import time
import random
from multiprocessing import Process,Queue
def producer(q):
for i in range(1,10):
time.sleep(random.random())
print('制作了%s包子。'%(i))
q.put(i)
def consumer(q,name):
while True:
time.sleep(3)
q_get = q.get()
if not q_get:break
print('%s吃了第%s个包子'%(name,q_get))
def run(p,c):
p_list = []
for i in range(p): #循环创建生产者
p1 = Process(target=producer, args=(q,))
p1.start()
p_list.append(p1)
for i2 in range(c): #循环创建消费者
Process(target=consumer, args=(q, 'xuan')).start()
for i3 in p_list: #等待生产者全部完成。
i3.join()
for i4 in range(c): #判断消费者全部消费完后退出。
q.put(None)
if __name__ == '__main__':
q = Queue(3) #允许队列的数量,可以有效减少排队过多而导致的占用大量内存问题。
run(3,1)
创建多个进程,输出子进程内容,在最后输出父进程。
from multiprocessing import Process
import time
import os
def func(a,):
print(a,os.getpid())
time.sleep(2)
if __name__ == '__main__':
e = []
for i in range(10):
P = Process(target=func,args=(i,))
P.start()
e.append(P) #把子进程内存地址记住
for i2 in e:i2.join() #然后循环所有子进程,等待所有子进程的结束
print('结束')

浙公网安备 33010602011771号