day9-进程池的使用
概述
之前不是说,多个进程间的内存都是独立的。在进程里面怎么也有锁的概念,为什么?虽然进程间都是独立运行的,但是他们共享同一块屏幕,所以为了避免打印出的数据不乱(加锁的情况下进程独占屏幕),所以加锁。
进程同步
用法:使用multiprocess模块中的Lock锁模块实现进程锁
#-*- coding:utf-8 -*- from multiprocessing import Process, Lock def f(l, i): l.acquire() #获取锁 print('hello world', i) l.release() #释放锁 if __name__ == '__main__': lock = Lock() #生成锁的实例 for num in range(10): #启动10个进程 p = Process(target=f, args=(lock, num)) #将锁对象作为参数传给子进程f p.start() #启动子进程 #运行输出 hello world 0 hello world 2 hello world 1 hello world 3 hello world 4 hello world 5 hello world 6 hello world 7 hello world 8 hello world 9 Process finished with exit code 0
进程池
作用:在使用Python处理并发任务时,最简单的模式就是主进程等待任务,当有新任务来到时,启动一个新的进程来处理当前任务。这种每个任务一个进程的处理方式,每处理一个任务都伴随着一个进程的创建,运行和销毁,如果进程的运行时间越短,创建和销毁的时间所占比重就越大,显然,我们应该尽量避免创建和销毁进程本身的额外开销,提高进程的运行效率。我们可以使用进程池来减少进程的创建和开销,提高进程对象的复用。
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用的进程为止。
进程池的两个方法
1.apply
功能:同步执行也就是串行执行
from multiprocessing import Process, Pool #导入模块 import time,os def Foo(i): time.sleep(2) print("in process:",os.getpid()) return i + 100 def Bar(arg): print('-->exec done:', arg) pool = Pool(5) #允许进程池里同时放入5个进程 for i in range(10): #启动10个进程 pool.apply(func=Foo, args=(i,)) #串行 print('end') pool.close() pool.join() # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭(不等进程执行完毕)。 #运行输出 in process: 32048 in process: 32049 in process: 32050 in process: 32051 in process: 32052 in process: 32048 in process: 32049 in process: 32050 in process: 32051 in process: 32052 end Process finished with exit code 0
解析:串行执行的只有进程池中的5个,其他5个进程被挂起了。当进程池中进程执行结束一个,另外5个进程就进去一个,如此反复执行。直到全部执行结束。
2.apply_async
功能:异步执行也就是并发执行
from multiprocessing import Process, Pool import time,os def Foo(i): time.sleep(2) print("in process:",os.getpid()) return i + 100 def Bar(arg): print('-->exec done:', arg) pool = Pool(5) #允许进程池里同时放入5个进程 for i in range(10): #启动10个进程 pool.apply_async(func=Foo, args=(i,)) #并行 print('end') pool.close() pool.join() # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭(不等进程执行完毕)。 #运行输出 end in process: 32114 in process: 32116 in process: 32117 in process: 32115 in process: 32118 in process: 32114 in process: 32116 in process: 32117 in process: 32115 in process: 32118 Process finished with exit code 0
解析:并行执行的只有进程池中的5个,其他5个进程被挂起了,等待5个同时执行结束,再放入进程池中另外5个,再同时执行。join()函数是等待所有进程池中的进程执行完毕后再关闭程序。没写的话,就是说不等进程执行结束与否,直接关闭程序。
在以上进程的并发方法中存在一个callback参数,它的作用可以实现回调,下面修改下例子
from multiprocessing import Process, Pool import time,os def Foo(i): time.sleep(2) print("in process:",os.getpid()) return i + 100 def Bar(arg): print('-->exec done:%s 回调函数Bar的PID:%s'%(arg,os.getpid())) pool = Pool(5) #允许进程池里同时放入5个进程 print("主进程的PID:",os.getpid()) for i in range(10): #启动10个进程 pool.apply_async(func=Foo, args=(i,),callback=Bar) #callback=回调 print('end') pool.close() pool.join() # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭(不等进程执行完毕)。 #输出 主进程的PID: 32163 end in process: 32166 in process: 32165 in process: 32164 in process: 32168 in process: 32167 -->exec done:101 回调函数Bar的PID:32163 -->exec done:102 回调函数Bar的PID:32163 -->exec done:100 回调函数Bar的PID:32163 -->exec done:104 回调函数Bar的PID:32163 -->exec done:103 回调函数Bar的PID:32163 in process: 32165 -->exec done:105 回调函数Bar的PID:32163 in process: 32166 in process: 32164 in process: 32168 -->exec done:106 回调函数Bar的PID:32163 -->exec done:107 回调函数Bar的PID:32163 -->exec done:108 回调函数Bar的PID:32163 in process: 32167 -->exec done:109 回调函数Bar的PID:32163 Process finished with exit code 0
解析:可以看出每个子进程执行结束后都执行了Bar()函数,通过结果可以发现并不是子进程调用了Bar()函数,而是主进程执行了回调Bar()函数。
应用场景
启动10个进程到备份服务器上备份数据,备份完后要写一条日志到数据库。那么就可以使用回调函数实现单个进程(主进程)连接数据库,而不是10个子进程或更多的子进程,每次都调用Bar()函数启动10个或更多的进程同时连接到数据库,这样就节约了资源,提高了运行效率。