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('结束')