详解进程

多任务

多任务就是同一时刻多个任务同时执行,例如开演唱会时明星一边唱歌一边跳舞,开车时眼睛看路手操作方向盘。这些都是多任务场景。
对于电脑来说多任务就是同时运行多个应用程序,例如qq、微信、浏览器等等同时在电脑上运行。

电脑多任务的原理

例如qq、微信、网易云音乐播放器3个应用程序能同时运行时因为CPU在多个应用程序之间高速切换的结果,当CPU切换到了QQ。就用0.01s时间
执行qq程序,然后在随机切换到其他应用程序在执行一段时间,CPU在多个程序之间快速王府执行,我们的肉眼根本感觉不到卡顿,导致我们的错觉感觉是同时运行的效果。如果电脑运行了多个程序有时候会出现卡顿现象是因为CUP切换不过来了。
在这里插入图片描述

单核、双核CPU介绍:

单核CPU指的是CPU中有一个核心(形象理解CPU是人的头部,核心是头部包含的大脑),用来处理程序。
双核/四核CPU就是CPU中有2个或者4个核心,(1个脑袋中长了2个大脑或者4个大脑),相当于有2个单核CPU或者是4个单核CPU
在这里插入图片描述

查看CPU:

电脑–>属性–>设备处理器–>处理器,有4分表示一个CPU中有4个核心。
在这里插入图片描述
3.30GHz表示运算速度,越高越好。例如1s中可以计算多少次。
4、在python中实现多任务有3种方式,进程、线程、协程。
三、进程
1、什么是进程
我们想通过网易云听歌,具体的过程应该是先找到网易云应用程序然后双击就会播放音乐。当我们双击时,操作系统将程序装载到内存中,操作系统为它分配资源,然后才能运行。运行起来的应用程序就称之为进程。也就是说当程序不运行的时候我们称之为程序,当程序运行起来他就是一个进程。通俗的理解就是不运行的时候是程序,运行起来是进程。
程序和进程的关系是:程序只有一个,但是进程可以有多个。
2、狭义定义:进程是正在运行的程序的实例
广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统操作系统中,进程即是基本的分配单元,也是基本的执行单元。
3、进程概念:
第一:进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region) 和 堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程中调用的指令和本地变量。
第二:进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行它的时候),它才能成为一个活动的实体,我们称其为进程。
进程是操作系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态情况,描述系统内部个道程序的活动规律引进的一个概念,所有多道程序设计操作系统都建立在进程的基础上

操作系统引入进程的概念

从理论角度看,是对正在运行的过程的抽象;
从实现角度看,是一种数据结构,目的在于清晰的刻画动态系统的在内部的规律,有效的管理和调度进入计算机系统主存储器运行的程序。

进程的特征

1、动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生动态消亡的。
2、并发性:任何进程都可以同其他进程一起并发执行
3、独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位:
4、异步性:由于进程间的相互制约,使进程具有执行的间断性。既进程按各自独立的、不可预知的速度向前推进
5、结构特征:进程由程序、数据和进程控制块三部分组成。
多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变。

进程与程序的区别

1、程序时指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
2、而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
3、程序可以作为一种软件资料长期存在
4、程序是永久的,进程是暂时的
**注意:**同一个程序执行两次,就会在操作系统中出现连个进程,所以我们可以同时运行一个软件,分别作不同的事情也不会混乱。

进程调度

想要多个进程交替运行,操作系统必须对这些进程进行调度,这个调度也不是随即进行的,而是需要遵循一定的法则,由此就有了进程的调度算法。
先来先服务的调度算法:
先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度。FCFS算法比较利于长作业(进程),而不利于短作业(进程)。由此可知,本算法适合于CPU繁忙型作业,而不利于I/O繁忙型的作业(进程)
时间片轮转法:
1、时间片轮转(Round Robin,RR)法的基本思路是让每个进程在就绪队列中的等待时间与享受服务器时间成比例。在时间片轮转法中,需要将CPU的处理时间分成固大小的时间片,例如,几十毫秒至几百毫秒。如果一个进程在被调度选中之后用完了系统规定的时间片,但又未完成要求的任务,则它自行释放自己所占有的CPU而排到就绪队列的末尾,等待下一次调度。
2、显然轮转法只能用来调度分配一些可以抢占的资源。这些可以抢占的资源可以随时被剥夺,而且可以将它们再分配给别的进程。CPU是可抢占资源的一种。单打印机等资源是不可抢占的。由于作业调度室对除了CPU之外的所有系统硬件资源的分配,其中包含有不可抢占的资源,所以组偶尔带哦度不使用轮转法。
3、在轮转法中,时间片长度的选取非常重要。首先,时间片长度的选择会直接影响到系统的开销和响应时间。如果时间片长度过短,则调度程序抢占处理机的次数增多。这将使进程上下文切换次数也大大增加,从而加重系统开销。反过来,如果时间片长度选择过长,例如,一个时间片能保证就绪队列中所执行时间最长的进程能执行完毕,则轮转法变成了先来先服务法。时间片长度的选择是根据对响应时间的要求和就绪队列中所允许最大的进程数来确定的。
4、在轮转法中,加入到就绪队列的进程有3种情况:
(1)一种是分给她的时间片用完,但进程还未完成,回到就绪队列的末尾等待下次的调度去继续执行
(2)另一种情况是分给该进程的时间片并未用完,只是因为请求I/O或由于进程的互斥与同步关系而被阻塞。当阻塞解除之后再回到就绪队列。
(3)新创建进程进入就绪队列。
如果对这些进程区别对待,给予不同的优先级和时间片从直观上看,可以进一步改善系统服务质量和效率。例如:我们可把就绪队列按照进程到达就绪队列的类型和进程被阻塞时的阻塞原因分成不同的就绪队列,每个队列按FCFS原则排列,各队列之间的进程有不同的优先级,但同一队列内优先级相同。这样,当一个进程再执行它的时间片之后,或从睡眠中被唤醒以及被创建之后,将进入不同的就绪队列。

多级反馈队列

1、前面介绍的各种用作进程调度的算法都有一定的局限性。如短进程优先的调度算法,仅照顾了短进程而忽略了长进程,而且如果并未指明进程的长度,则短进程优先和给予进程长度的抢占式调度算法都将无法使用。
2、而多级反馈队列调度算法则不必事先知道各种进程所需的执行时间,而且还可以满足各种类型进程的需要,因而他是目前被公认的一种较好的进程调度算法。在采用多级反馈队列调度算法的系统中,调度算法的实施过程如下所述。
(1)、应设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各个队列的优先权主机降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,。。。,第i+1个队列的时间片要比第i个队列的时间片长一倍。
(2)、当一个新进程进入内存后,首先将它们放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二个队列的末尾,再同样的按FCFS原则等待调度执行;如果他在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,。。。如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,在第n队列变采取按时间片轮转的方式运行。
(3)、仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第i ~(i-1))中的任何一个队列,则此时新进程将抢占正在运行进程的处理机,既由调度程序把正在运行的进程放回到第i队列的末尾
,把处理机分配给新到的高优先权进程。

进程的并行与并发

1、并行:并行是指两者同时执行,比如赛跑,两个人都在不停的往前跑;(资源够用,比如三个线程,四核的cpu)
2、并发:并发指的是资源有限的情况下,两者交替轮流使用资源,比如一段路(单核cpu资源)同时只能过一个人,A走了一段后,让给B,B用完继续给A,交替使用,目的是提高效率。
区别:
并行是从微观上,也就是在一个精确的时间片刻,有不同的程序在执行,这就要求必须有多个处理器。
并发是从宏观上,在一个时间段上可以看出是同时执行的,比如一个服务器同时处理多个session

同步异步阻塞非阻塞

进程的状态:
进程三态状态换图
在这里插入图片描述
在程序运行的过程中。由于被操作系统的调度算法控制,程序会进入几个状态:就绪,运行和阻塞
(1)、就绪(Ready)状态
当程序及分配到出CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态就呈现为就绪状态;
(2)、执行/运行(Running)状态当进程已获得处理机,其程序正在处理机上执行,此时的进程为执行状态
(3)阻塞(Blocked)状态正在执行的进程,由于等待某个时间发生无法执行时,便放弃处理机而处于阻塞状态。引起进程的事件有多种可能,例如等待I/O完成,申请缓冲区不能满足、等待信号等
在这里插入图片描述

同步和异步

概念
1、所谓同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功。失败都失败,两个任务的状态可以保持一致。
2、所谓异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列

举例:
比如去银行办理业务,可能会有两种方式:
第一种:选择排队等待;
第二种:选择取一个小纸条上面有我的号码,等到排到我这一号时由柜台的人通知我轮到我去办理业务了;

第一种:前者(排队等候)就是同步等待消息通知,也就是我要一直等待银行办理业务情况;
第二种:后者(等待别人通知)就是异步等待消息通知。在异步消息处理中,等待消息通知者(在这个例子中就是等待办理业务的人)往往注册一个回调机制,在所等待的事件被触发时由触发机制(在这里是柜台的人)通过某种机制(在这里是写在小纸条上的号码。喊号)找到等待该事件的人。

阻塞与非阻塞

概念 :阻塞和非阻塞这两个概念与程序(线程)等待消息通知消息(无所谓同步或者异步)时的状态有关。也就是说阻塞与非阻塞主要是程序(线程)等待消息时的状态角度说的
举例
继续上面的那个例子,不论是排队还是使用号码等待通知,如果在这个等待的过程中,等待着除了等待消息通知外不能做其它的事情,那么该机制就是阻塞的,表现在程序中,也就是该程序一直阻塞在该函数调用处不能继续往下执行。
注意:同步非阻塞形式实际上是效率低下的,想象以下你一边打着电话一边还需要抬头看到底队伍排到你了没有。如果把打电话和观察排队的位置看成是程序的两个操作的话,这个程序需要在这两种不同的行为之间来回的切换,效率可想而知是低下的;
而异步非阻塞形式却没有这样的问题,因为打电话是你(等待着)的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不同的操作中来回切换。

同步/异步与阻塞/非阻塞

1、同步阻塞形式
效率最低。拿上面的例子来说,就是你专心排队,什么别的事都不做。
2、异步阻塞形式
如果在银行等待办理业务的人采用的是异步的是方式去等待消息被触发(通知),也就是领了一张小纸条,假如在这段时间里他不能离开银行做其它的事情,那么很显然,这个人被阻塞在了这个等待的操作上面;
异步操作是可以被阻塞的,只不过它不是在处理消息时阻塞,而是在等待消息通知时被阻塞。
3、同步非阻塞形式。
实际上是效率底下的。
想象一下你一边打着电话一边还需要抬头看到底队伍排到了你没有,如果把打电话和观察排队的位置看成是程序的两个操作的话,这个程序需要在这个两种不同行为之间来回的切换,效率可想是低下的。
4、异步非阻塞形式
效率更高,
因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不同的操作中来回切换。
比如说,这个突然发觉自己烟瘾犯了,需要出去抽根烟,于是他告诉大堂经理说,排到我这个号码的时候麻烦到外面通知我一下,那么他就没有被阻塞在这个等待的操作上面,自然这个就是异步+非阻塞方式了。
很多人会把同步和阻塞混淆,是因为很多时候同步操作会以阻塞的形式表现出来,同样的,很多人也会把异步和阻塞混淆,因为异步操作一般都不会在真正的IO操作处被阻塞。
在这里插入图片描述
进程的创建与结束
进程的创建
但凡是硬件,都需要有操作系统去管理,只要有操作系统,就有进程的概念,就需要有创建进程的方式,一些操作系统只为一个应用程序设计,比如微波炉中的控制器,一旦启动微波炉,所有的进程都已经存在。
而对于通用系统(跑很多应用程序),需要有系统运行过程中创建或者撤销进程的能力,主要分为4种形式创建新的进程:
1、系统初始化(查看进程linux中用ps命令,windows中用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台并且只在需要时才唤醒的进程,称为守护进程,电子邮件、web页面、新闻、打印)
2、一个进程在运行过程中开启了子进程(如nginx开启多进程,os.fork,subprocess.Popen等)
3、用户的交互式请求,而创建一个新进程(如用户双击暴风影音)
4、一个批处理作业的初始化(只在大型机的批处理系统中应用)
5无论哪一种,新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的。

进程的结束:

1、正常退出(自愿,如用户点击交互式页面的❌,或者程序执行完毕调用发起系统调用正常退出,在linux中用exit,在windows中用ExitProcess)
2、出错退出(自愿,python a.py中a.py不存在)
3、严重错误(非自愿,执行非法指令,如引用不存在的内存,1/0等,可以捕捉异常,try…except…)
4、被其他进程杀死(非自愿,如kill-9)

进程是系统进行资源分配和调度的基本单位。

python中的进程操作

multiprocess模块

仔细说来,multiprocess不是一个模块而是Python中一个操作、管理进程的包。之所以叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的所有子模块。由于提供的子模块非常多,为了方便归类记忆,将这部分大致分为四个部分:
1、创建进程部分,
2、进程同步部分,
3、进程池部分,
4、进程之间数据共享。

multiprocess.process模块

process模块介绍

process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建

Process( [group  [,  target[,name [,args [ , kwargs] ] ] ] ]),

由给类实例化得到的对象,表示一个子进程中的任务(尚未启动)

强调:
1、需要使用关键字的方式来指定参数
2、args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
参数介绍:
group参数未使用,值始终未None
target表示调用对象,既子进程要执行的任务
args表示调用对象的位置参数元组,args=(1,2,‘egon’)
kwargs表示调用对象的字典,kwargs= {‘name’:‘egon’,‘age’:‘18’}
name为子进程的名称

方法介绍

1、p.start():启动进程,并调用该子进程中的p.run( )
2、p.run() :进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
3、p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁。
4、p.is_alive():如果p任然运行,返回True
5、p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join主start开启的进程,而不能join住run开启的进程

属性介绍

1、p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
2、p.name:进程的名称
3、p.pid:进程的pid
4、p.exitcode:进程在运行时为None、如果为-N,表示被信号N结束(了解即可)
5、p.authkey:进程的身份验证键,默认是os.urandom()随机生成的32位字符的字符串。这个键的用途是为了涉及网络连接的底层进
程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)
在winodws中使用Process模块需要注意的事项
在windows操作系统中由于没有fork(linux操作系统中创建进程的机制),在创建子进程的时候会自动import启动它的这个文件,而import的时候有执行了整个文件。因此如果将Process()直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if name == 'mian’判断保护起来,import的时候,就不会递归运行了。

process模块创建进程

在一个python进程中开启子进程,start方法和并发效果

import time
from multiprocessing import Process # 这个使用的是Process类创建

def f(name):
    print('hello',name)
    print('我是子进程')
if __name__ == '__main__':

    p = Process(target=f,args=('赵胜杰',)) # 注意是args;
                                     # args表示调用对象的位置参数元组,args=(1,2,'egon',)
    p.start()
    time.sleep(1)
    print('执行主进程的内容了')

在这里插入图片描述
还有一种写法

import multiprocessing
def task(arg):
    print(arg)

def run():
    for i in range(10):
        p = multiprocessing.Process(target=task,args=(i,))
        p.start()
if __name__ == '__main__':
    run()

在这里插入图片描述

from multiprocessing import Process
def func(arg):
    print(arg)
if __name__ == '__main__':
    for i in range(10):
        p = Process(target=func,args=(i,))
        p.start()
'''
子进程的执行顺序不是根据启动顺序决定的
而且是逐渐增加,不是全部在同一时间出现没说明
每个线程都是独立的,数据间不共享,线程是每个逐渐增加
'''

在这里插入图片描述

进程之间数据隔离
# 进程之间的数据隔离
from multiprocessing import Process

def work():
    global n
    n = 0
    print('子进程内:',n)
if __name__ == '__main__':
    n =100
    p = Process(target=work)
    p.start()
    print('主进程内:',n)

在这里插入图片描述

创建多进程

1、不使用多进程实现控制台先打印唱歌然后在打印跳舞。

import time
def sing():
    '''唱歌3s中'''
    for i in range(3):
        print('---正在唱歌---')
        time.sleep(1)
def dance():
    '''跳舞3s中'''
    for i in range(3):
        print('---正在跳舞---')
        time.sleep(1) 
if __name__ == '__main__':
    sing()
    dance()
    # 花费了6秒

在这里插入图片描述

3、使用进程让唱歌和跳舞一起执行

def sing():
    '''唱歌3s中'''
    for i in range(3):
        print('---正在唱歌---')
        time.sleep(1)
def dance():
    '''正在跳舞'''
    for i in range(3):
        print('---正在跳舞---')
        time.sleep(1)
def main():
    p1 = multiprocessing.Process(target=sing)
    p2 = multiprocessing.Process(target=dance)
    p1.start() # 开始执行进程p1
    p2.start() # 开始执行进程p2
    # 开启了2个子进程p1,p2

if __name__ == '__main__':
    main()
    # 花费了3秒提高了程序执行的效率

在这里插入图片描述
程序理解:
主进程从main()开始执行,执行main函数体,当执行到p1.start()时,创建一个子进程,p1子进程中的代码和主进程相同,只是程序执行的开始是sing函数体。
主进程执行到p2.start()时,同样复制一份主进程代码从danc函数体开始执行
在这里插入图片描述

join()方法
import time
from multiprocessing import Process

def f(name):
    print('hello',name)
    time.sleep(1)
    print('我是子进程')

if __name__ == '__main__':
    p = Process(target=f,args=('赵胜杰',))
    p.start()
    # p.join()
    print('我是父进程')
    '''
    不使用join()方法的时候,先执行主进程,再执行子进程,子进程在等待主进程执行完后再执行
    '''

在这里插入图片描述

import time
from multiprocessing import Process

def f(name):
    print('hello',name)
    time.sleep(1)
    print('我是子进程')

if __name__ == '__main__':
    p = Process(target=f,args=('赵胜杰',))
    p.start()
    p.join()
    print('我是父进程')

在这里插入图片描述

在多进程下的join
# 多进程下
import time
from multiprocessing import Process

def f(name):
    print('hello',name)
    time.sleep(1)

if __name__ == '__main__':
    p_lst = []
    for i in range(5):
        p = Process(target=f,args=('fioergh',))
        p.start()
        p_lst.append(p)
        p.join()# 这里选择开启和关闭join()方法
        # [p.join() for p in p_lst]
    print('父进程在执行')
    

在这里插入图片描述
在这里插入图片描述

总结:join() 主进程等待子进程完成,才继续执行,参数为最多等待时间
强调:是主线程处于等的状态,而p是处于运行的状态

查看主进程和子进程的进程号

import os
from multiprocessing import Process
def f(x):
    print('子进程id:',os.getpid(),'父进程id:',os.getppid())
    return x *x
if __name__ == '__main__':
    print('主进程id:',os.getppid()) 
    p_lst = []
    for i in range(5):
        p = Process(target=f,args=(i,))
        p.start()

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值