一:什么是进程
当代码编译完成,在没有运行时称之为程序。正在运行着的代码,称之为进程。
二:进程的创建
1:fork
2: multiprocessing
三:Fork
fork 为unix环境下创建子进程,封装在OS模块中。
例1:fork()创建子进程
代码讲解:当程序运行至os.fork()会创建出一个子进程,然后复制父进程的所有信息到子进程中,即父进程与子进程均打印一次"--test--"
例2:fork()返回值
代码讲解:当程序运行至os.fork()时,会创建一个子进程,并且返回一个非0的值,而此时父进程的返回的值为0。
例3:进程PID
代码讲解:os.getpid() 获取当前进程的PID, os.getppid()获取其子进程的PID。
例4:多进程修改全局变量
代码讲解:多进程中,每个进程中所有的数据(包括变量)都各自拥有一份,互相不影响
多次Fork说明:
子父进程的运行顺序没有规律,完全取决于操作系统的算法
四:multiprocessing
由于fork只能在unix上运行,在windows上无法运行,而Python为跨平台语言,即在引进multiprocessing模块。
例1:Process
代码讲解: 其中使用Process创建一个实例子进程,用start()方法启动,其中join()方法可以等待子进程结束后再运行后续代码,通常用于进程间的同步。
Process语法结构:
Process([group[,target[,name[,args.[kwargs]]]])
target:表示这个进程实例所调用的对象
args:表示调用对象的位置参数元组
kwargs:表示调用对象的关键字数字典
name :为当前进程实例别名
group:大多数情况下用不到
Process常用方法:
is_alive():判断进程实例是否还在执行
join([timeout]):是否等待进程实例执行结束,或等待多少秒
start():启动进程实例(创建子进程)
run():如果没有给定target参数,对这个对象调用start()方法时,就将执行对象中的run()方法
terminate():不管任务是否完成,立即终止
例2:Processs
代码解释: 其中运行至“test = Process(target=Tets_Process,args=(5,))”时,创建一个子进程实例,且通过args将5以元组的形式传递给函数中,"test.start()":启动子进程,“test.join(3)”让子进程堵塞3秒,先运行后续代码。
例3:创建Process子类,调用run()方法
代码讲解:NewProcess继承Process类方法,当创建test实例对象事,并没有传对应的方法,会自动调用实例对象中的run()方法。
Pool进程池
当需要创建的子进程数量不多时,可以直接使用multiprocessing中的Process动态生成多个进程,但如果上百进程,则可以使用multiprocessing模块中Pool方法; Pool初始化时,可以指定一个最大的进程数,当有新的请求提交到Pool中时,如果池还没满,那么就会创建一个新的进程用来执行请求,但如果池中的进程已经达到指定的最大值,那么该请求就会等待,知道池中有进程结束,才会有新的进程执行。
例4:Pool进程池
代码解释:当代码运行到"po = Pool(2)"时,初始化进程池,且最大进程池为2;for循环,通过apply_async(workd,(i,))创建worker子进程实例,“po.close()”关闭Pool进程池,使其不在接受新任务; join()主进程堵塞,等待子进程的退出。由于进程池最大为2,急运行结果需要依次排队等待进程。
Pool常用方法:
apply_async(func[,args[,kwds]]]):使用非阻塞调用func,args为传递给func的参数列表,kwds为传递给func的参数列表
apply(func[,args[,kwds]]):使用阻塞方法调用func
close():关闭Pool,使其不再接受新的任务
terminate():不管任务是否完成,立即终止
join():主进程阻塞,等待子进程退出,必须在close或terminate之后使用
例5:Pool堵塞试进程
代码解说: Pool非阻塞试进程和阻塞试进程的区别在于,添加进程是否使用apply_async与apply, 非阻塞试为最大进程池并行,阻塞为,每次等待子进程运行完成之后,运行下一个子进程。
Queue进程间通信:
Queue的常用方法:
Queue.qsize():返回当前队列包含的消息数量;
Queue.empty():如果队列为空,返回True,反正False
Queue.full():如果队列满了,返回True,反正False
Queue.get([block[,timeout]]):获取队列中一条消息,然后将其从队列中移除;block默认为True,
(1)如果True使用默认值,且没有设置timeout,消息队列为空,此时程序会被阻塞(停在读取状态),直到消息队列读到消息为止,如果设置了timeout,则会等待timeout秒,若还未读到消息,则会抛出Queue.Empty异常。如果设置block为False,消息队列如果为空,则会立刻抛出Queue.Empty异常,
(2)如果block的值为False队列为空,则会立马抛出异常
Queue.get_nowait():相当于Queue.get(False)
Queue.put(itme,[block[,timeout]):将itme信息写入队列,block默认值为True
(1)如果True使用默认值,且没有设置timeout时间,若消息队列已满,此次程序会被阻塞(停在写入状态),直到队列空闲为止,若设置了timeout时间,则会等待timeout秒,若此时还没有空间,则会抛出Queue.Full异常。
(2)如果使用False,且没有设置timeout时间,若消息队列已满,则会立即抛出异常。
Queue.put_nowait():等价于Queue.put(False)
例6:Queue队列,先进先出
代码解说:使用Queue()创建一个无限制队里,使用Process创建两个实例子进程,通过if判断队列是否为空或爆满状态,来往队列中put和get.
例7:进程池中的Queue
代码解说:Queue在进程池中使用,需要使用Manager().Queue()进行初始化, 在导入模块时需要导入Manager()
多中方式的比较:
1: 在unix系统上,可以使用fork创建子进程,windows上不能使用fork
2:Process适用于数量较少的子进程创建
3:Pool适用于数量较多,或不确定子进程数时使用的进程池。
4:进程之间的通信使用Queue
5:进程池之间的通信,需要先使用Manager().Queue()对队里进行初始化