多任务编程—多进程

什么是多任务编程?

  多任务编程其实和计算机系统内核有关,通过程利用多个计算机内核同时执行程序,以此来提升程序执行的效率。

  多任务编程其中包括,多进程、多线程和多协程,这三种多任务编程各有各的优点和缺点,本章就来讲一下进程。

首先,什么是进程?

  进程就是一次执行的程序,最直接的例子就是我们常用的任务管理器,任务管理器,任务管理中每一个运行中的程序其实都叫进程。  

  在编程中进程是程序的一种动态过程的描述,进程在执行过程中会占用我们计算机内的资源,但是每个进程都有自己的生命周期(进程在系统内核所存在的时间),生命周期结束后进程会自动销毁。

每个进程都会有一个独立的虚拟内存(4g),使其在运行过程中独立进行。

  

   进程的在编程中的作用是为了处理程序并发的一种方式或者说是方法可能会更贴切一点,进程适用于同一程序每次都执行不同的任务,我们称之为多进程,多进程中分为父进程和子进程。

  创建进程的方式一共有两种:

  第一种:导入 import os 模块 ,利用 os 模块中的fork( )内建函数来创建子进程,fork( )创建并不难,但是最值得注意的是fork( )这个函数返回值,这个函数相比于其他函数有些特殊,fork( )自身拥有三种返回值,分别是小于0,等于0 和大于0的pid(系统对进程的唯一编号)数字,这三种分对应着不同的处理结果。

小于0 —【建子进程失败

等于0 —【创建子进程成功】

大于 0—【父进程】

  在创建过程中一般情况下都会给fork( )的返回值设定一个if判断语句,以此来确认子进程是否创建成功,至于这个返回值的判断机制是完全由系统内核经过自动计算后所返回的,这个是机制是不可抗的。下面我们就来创建一个子进程实例,代码如下:

import os


print('---------分割线-----------')

pid = os.fork()#开始创建子进程
print('fock返回的pid号',pid)

if pid < 0:
print('创建子进程失败')

elif pid == 0:
print('创建子进程成功')

else:
print('这是父进程') #父进程会先执行

运行结果:

 

  由此我们可以出所返回值,运行程序中分别生成了一个大于0和一个 等于0数字,说明子进程已经创建成功,因此小于0的值并不会被生成出来。我们可以在已创建的子进程中可以添加自己所需要的内容,让每个程序都在一个子进程中独立分开执行,达到并发效果。那么在创建成功后子进程都会包含什么?

  子进程会复制父进程中所有的代码,但是子进程不会执行所复制的所有代码段,它只会运行所创建的子进程代码段,父进程的代码段子进程并不会去运行。(紫色的部分代表分别要执行的代码段

  如下图:

  如果多个进程在同时抢占计算机资源时应该怎么办,实际上会使计算机对所有进程进行一种快速切换的机制,a进程和b进程同时执行的时候谁先谁后不可定,但是他们一定是交叉运行的。在运行a进程时计算机会把资源 分配给a去使用,但是这时候b进程突然运行,计算机就会把资源在分配给 b去运行,这个过程就是计算机内部的资源分配机制。

  但是使用fork( )创建多个子进程就会非常麻烦,子进程中嵌套二级子进程以此类推,这样不停的去使用if嵌套在结构上是一种很不好的一种体验,所以在python中有一个更加清晰简洁的一种创建方式,使用multiprocessing模块中的Process去创建多个子进程。

 

target=‘目标函数'

agrs=(’参数',)#元组传参

kwargs ={'key':‘value’}#字典传参

name='命名' 

start( ) #启动子进程

join( ) #子进程的回收

  它的代码结构非常的清晰,但是相对于fork( ) 它增加了两个机制,一个是启动函数strart( ) 和阻塞等待子进程回收机制 join( ),只有所有的子进程全部执行完毕后才会回收,这两个函数分别控制子进程的开始和结束,其中jion( )这个函数里面可以添加参数(秒),在输入相应的数字时会自动开始倒计时等待,如果在倒计时结束后join( )机制会执行,但是这个时候join( )子进程并没有结束,父进程检测到没有join阻塞也会执行,这个时候子进程就变为了孤儿进程,孤儿进程在执行完毕后,会在系统中找一个继父并结束,也就是说这个继父收养了这个孤儿进程,使其能够正常结束。

 进程的三态和五态:

三态

就绪态 : 进程具备运行条件,等待系统分配处理器运行
运行态 : 进程占有cpu处于运行的状态
等待态 : 又称为阻塞态,睡眠态,指进程暂时不具备运

五态

新建态 : 创建一个进程,获取资源,直接表现为运行一个程序,或者在程序中创建新的进程

终止态 : 进程执行结束,资源回收过程

  进程池:

  进程池:一次创建多个进程批量执行多个任务的程序,频繁大量的任务建议使用进程池  比如一个进程创建后每两秒就销毁,但是任务两很大需要大量重复这个创建销毁的过程,这种情况下就建议使用进程池去处理 ,打个比方,一辆车上需要搬运200个西瓜才能达到运送标准,在搬运西瓜给货车的同时有n个人一起去完成这个任务,每次填装的西瓜数量是n个,可以很快的就将这200个西瓜装载完毕,大大的 节省了时间成本,所谓的进程池的执行方式就是这个概念。

  进程池每次处理事件时不会像普通进程那样每次处理完事件就会回收,进程池只有处理完所有的事件才会统一回收,再打个比方给大家:

我们创建了四个进程,事件需要执行20次,那么这个进程池每次可以处理1-4这四次事件后并不会回收,等到需要再次处理5-8这四个进程时在开启,随后再次关闭,这违背了进程池设计的初衷,进程池内只有执行完这20次的所有事件后才会结束销毁。

  创建进程池使用 Pool函数,形式上和创建进程大同小异,首先需要创建一个事件函数,准备执行,使用Pool(processes=n)来创建n个进程,等待事件的处理,进程池不需要使用start( ) 函数来启动进程,进程池内有它特有的处理事件的机制,因为在在进程池创建完成后进程就已经是处在一个等待运行的状态了,将事件放入后,进程会立即执行所放入的事件。

  异步处理和同步处理。

  异步处理:apply_async( ) 第一个参数为所执行的目标函数,第二个参数为实参,实参的传递方式可以选择元祖传参和字典传参,在调用函数和传递参数时不需要通过target和args调用, 异步处理会抢占时间片,可以理解成异步处理是一种无序随机运行。

  同步处理:apply( ) 同步处理的调用和传递实参的方式和异步处理相同,二者唯一的区别就是一个是有序运行,一种是无序运行。

  close( )

  进程池中的join( )同样是等待进程回收,这个之前就已经说过来了,就不在细说,我们重点来看一下这个close( ), close关闭并不是终止进程池运行,而是‘关闭’进程池使其不在接受其他新的事件元素 ,并不是所谓的终止运行。

  map函数 

  进程池中可以使用map函数对进程池中的数据进行处理,map函数我们在接触高阶函数的时候就已经接触过了,但是在这里还有一些小小的不同。

  以上代码所示:在进程池中使用map处理元素 ,第一个参数为需要调用 的目标函数,第二个位置是一个迭代器,将迭代对象传入事件进行处理,因为 l 这个跌迭对象拥有6个元素,但是我们的进程池只有三个进程(每处理一次让他停一秒,方便我们观察),所以每秒钟只能处理三个元素,两秒后处理完毕,最终将结果打印在终端上。

  注:如果在多进程中需要让第一个子进程完全执行完毕后,在执行第二个子进程,以此类推,这样的情况可以考虑使用进程所 Lock或Event去设置子进程的阻塞状态,改变执行效果。

 

猜你喜欢

转载自www.cnblogs.com/Jimmy1995/p/9323177.html