Python——multiprocessing(进程模块)
注意点:
1. 各操作系统中创建进程的方法不同。运行方式也有所不同。
windows创建进程:
执行开启进程的代码
1. Process实例化后,.start()来创建一个进程,并告诉系统,创建完进程后请执行Process内的target所指定的函数。
2. 由于是进程之间数据无法交互的问题,那么windows会以import的方式来将父进程的代码导入到子进程中,并运行。
3. 如果此时没有__name__判断语句,那么将会继续执行Process语句进行子进程的创建。反复创建直到报错。
IOS Linux操作系统创建进程:
是使用fork来将父进程代码运算后得出的值进行直接的拷贝(将内存地址直接拷贝),而不是在子进程中再运行一遍。
使用multiprocessing模块来创建一个进程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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,))
必须是元组形式,如果传一个参数,加逗号,
1 2 3 4 5 6 7 8 9 10 | 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
将子进程更改为守护进程,特点是随着主进程的代码结束而结束。如果有守护进程或非守护进程,那么当主进程结束时,先结束守护进程,然后等待其他子进程结束后回收资源并关闭。
1 2 3 4 5 6 7 8 9 | 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,因为系统还没来得及进行关闭进程。
1 2 3 4 5 6 7 8 9 | def func1(): print ( '这是func1' ) time.sleep( 0.5 ) if __name__ = = '__main__' : P1 = Process(target = func1) P1.start() print (P1.is_alive()) print ( '结束' ) |
join()
将异步非阻塞更改为阻塞状态,等待所有的子进程结束后,再将主进程向下执行。典型的同步阻塞状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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()
结束进程,异步非阻塞状态。发送给计算机后,会继续执行后面的命令。
1 2 3 4 5 6 7 8 9 10 11 | def func1(): print ( '这是func1' ) time.sleep( 0.5 ) if __name__ = = '__main__' : P1 = Process(target = func1) P1.start() P1.terminate() print (P1.is_alive()) print ( '结束' ) |
在面向对象中创建进程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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. 保证数据安全或不考虑有影响代码速率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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
当主进程需要让子进程返回数据,并不占用主进程的向下业务时,就需要让子进程运行完成后,返回一个值。
1 2 3 4 5 6 7 8 | 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时会存在数据丢失现象,能不用尽量不使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 |
实例:
生产者消费者模型:
把一个生产数据,并且处理数据的过程进行解耦,让生产数据的过程和处理数据的过程达到一个工作效率上的平衡。将大程序分解成多个小功能进行处理。并使用队列来对内存队列进行控制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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 ) |
创建多个进程,输出子进程内容,在最后输出父进程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 ( '结束' ) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)
2019-03-15 MySQL——用户与密码
2019-03-15 MySQL——安装