• 多进程 与并发


    1.进程与程序

      进程:是正在进行的程序;

      程序:是一堆代码,当这堆代码被系统加载到内存执行时,产生进程

      注:一个程序是可以产生多个进程,就像我们可以同时运行多个qq,会形成多个进程

    2.PID 与PPID

      pid:进程编号,用于区分多ge进程个;

      ppid:当前进程的父进程的进程编号,

      可以用os模块来获取pid/ppid        eg:       os.getpid()              os.getppid()

      如果在pycharm中运行py文件,那pycharm就是这个python.exe的父进程,如果从cmd运行py文件,那cmd就是python.exe的父进程

    3.进程与程序
     1.并发与并行 

      并发:指的是多个事件同时发生,本质是任务间的切换给人的感觉是同时在进行,称之为伪并行   例如洗衣服和做饭,同时发生了

      并行:指的是多个事件同时进行种,例如一个人在写代码另一个人在写书,这两件事件是同时在进行的,一个人是无法真正的并行执行任务

     2.阻塞与非阻塞,是程序的状态

      阻塞状态:是程序遇到io操作,比如sleep,print,input,导致后续的代码不能被CPU执行

      非阻塞状态:与之相反,表示程序正在被CPU执行

     3.进程的三种状态间的转换

      就绪 到  执行  要 进程调度    ,   执行 到 阻塞  要有io  ,                          阻塞 到就绪 要完成io

                    执行 到 就绪  cpu运行过长,时间片用完

      注:多道技术会在进程执行时间过长或遇到IO时自动切换其他进程,意味着IO操作与进程被剥夺CPU执行权都会造成进程无法继续执行

     4.进程的创建

    1. 系统初始化(查看进程linux中用ps命令,windows中用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台并且只在需要时才唤醒的进程,称为守护进程,如电子邮件、web页面、新闻、打印)

    2. 一个进程在运行过程中开启了子进程(如nginx开启多进程,os.fork,subprocess.Popen等)

    3. 用户的交互式请求,而创建一个新进程(如用户双击暴风影音)

    4. 一个批处理作业的初始化(只在大型机的批处理系统中应用)

      关于创建的子进程,UNIX和windows

      1.相同的是:进程创建后,父进程和子进程有各自不同的地址空间(多道技术要求物理层面实现进程之间内存的隔离),任何一个进程的在其地址空间中的修改都不会影响到另外一个进程。

      2.不同的是:在UNIX中,子进程的初始地址空间是父进程的一个副本,提示:子进程和父进程是可以有只读的共享内存区的。但是对于windows系统来说,会重新加载程序代码。

     

     5.进程的销毁

    1. 正常退出(自愿,如用户点击交互式页面的叉号,或程序执行完毕调用发起系统调用正常退出,在linux中用exit,在windows中用ExitProcess)

    2. 出错退出(自愿,python a.py中a.py不存在)

    3. 严重错误(非自愿,执行非法指令,如引用不存在的内存,1/0等,可以捕捉异常,try...except...)

    4. 被其他进程杀死(非自愿,如kill -9)

    进程的层次结构

          无论UNIX还是windows是先相同之处都是 需要将数据copy一份给子进程 这样子进程才知道要干什么  

      linux 会将父进程的所有数据 完全copy , 
      windows 会copy 一部分数据 同时会导入py文件来执行 这样一来递归开进程
      linux 拿到父进程知道代码位置 继续执行
      建议都加上判断 可以保证两个平台都能用
    1. 在UNIX中所有的进程,都是以init进程为根,组成树形结构。父子进程共同组成一个进程组,这样,当从键盘发出一个信号时,该信号被送给当前与键盘相关的进程组中的所有成员。

    2. 在windows中,没有进程层次的概念,所有的进程都是地位相同的,唯一类似于进程层次的暗示,是在创建进程时,父进程得到一个特别的令牌(称为句柄),该句柄可以用来控制子进程,但是父进程有权把该句柄传给其他子进程,这样就没有层次了。

     

    6.python中开启子进程的两种方式

    方式1:

    实例化Process类

    from multiprocessing import Process
    import time

    def task(name):
       print('%s is running' %name)
       time.sleep(3)
       print('%s is done' %name)
    if __name__ == '__main__':
       # 在windows系统之上,开启子进程的操作一定要放到这下面
       # Process(target=task,kwargs={'name':'egon'})
       p=Process(target=task,args=('jack',))
       p.start() # 向操作系统发送请求,操作系统会申请内存空间,然后把父进程的数据拷贝给子进程,作为子进程的初始状态
       print('======主')

    方式2:

    继承Process类 并覆盖run方法

    from multiprocessing import Process
    import time

    class MyProcess(Process):
       def __init__(self,name):
           super(MyProcess,self).__init__()
           self.name=name

       def run(self):
           print('%s is running' %self.name)
           time.sleep(3)
           print('%s is done' %self.name)
    if __name__ == '__main__':
       p=MyProcess('jack')
       p.start()
       print('主')

    需要注意的是

    1.在windows下 开启子进程必须放到__main__下面,因为windows在开启子进程时会重新加载所有的代码造成递归创建进程

    2.第二种方式中,必须将要执行的代码放到run方法中,子进程只会执行run方法其他的一概不管

     

    进程间内存相互隔离

     

    7.join函数

    调用start函数后的操作就由操作系统来玩了,至于何时开启进程,进程何时执行,何时结束都与应用程序无关,所以当前进程会继续往下执行,join函数就可以是父进程等待子进程结束后继续执行

    案例1:

    from multiprocessing import Process
    import time

    x=1000

    def task():
       time.sleep(3)
       global x
       x=0
       print('儿子死啦',x)
    if __name__ == '__main__':
       p=Process(target=task)
       p.start()

       p.join() # 让父亲在原地等
       print(x)

    案例2:

    from multiprocessing import Process
    import time,random

    x=1000

    def task(n):
       print('%s is runing' %n)
       time.sleep(n)

    if __name__ == '__main__':
       start_time=time.time()

       p1=Process(target=task,args=(1,))
       p2=Process(target=task,args=(2,))
       p3=Process(target=task,args=(3,))
       p1.start()
       p2.start()
       p3.start()

       p3.join() #3s
       p1.join()
       p2.join()

       print('主',(time.time() - start_time))

       start_time=time.time()
       p_l=[]
       for i in range(1,4):
           p=Process(target=task,args=(i,))
           p_l.append(p)
           p.start()
       for p in p_l:
           p.join()
       
       print('主',(time.time() - start_time))

    Process对象常用属性

    from multiprocessing import Process
    def task(n):
       print('%s is runing' %n)
       time.sleep(n)

    if __name__ == '__main__':
       start_time=time.time()
       p1=Process(target=task,args=(1,),name='任务1')
       p1.start() # 启动进程
       print(p1.pid) # 获取进程pid
       print(p1.name) # 获取进程名字
       p1.terminate() # 终止进程
       p1.join() # 提高优先级
       print(p1.is_alive()) # 获取进程的存活状态
       print('主')
    
    

    孤儿进程与僵尸进程

    孤儿进程指的是开启子进程后,父进程先于子进程终止了,那这个子进程就称之为孤儿进程      是无害的

    例如:qq聊天中别人发给你一个链接,点击后打开了浏览器,那qq就是浏览器的父进程,然后退出qq,此时浏览器就成了孤儿进程

    孤儿进程是无害的,有其存在的必要性,在父进程结束后,其子进程会被操作系统接管。

     

    僵尸进程指的是,当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被操作系统接管,子进程退出后操作系统会回收其占用的相关资源!

    僵尸进程的危害:,子进程先结束,保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生[僵死进程],将因为没有可用的进程号而导致系统不能产生新的进程。

     

     

     

  • 相关阅读:
    linux库
    java实现第七届蓝桥杯路径之谜
    java实现第七届蓝桥杯路径之谜
    java实现第七届蓝桥杯路径之谜
    java实现第七届蓝桥杯打印数字
    java实现第七届蓝桥杯打印数字
    java实现第七届蓝桥杯打印数字
    java实现第七届蓝桥杯打印数字
    java实现第七届蓝桥杯七星填数
    java实现第七届蓝桥杯七星填数
  • 原文地址:https://www.cnblogs.com/wyf20190411-/p/10957557.html
Copyright © 2020-2023  润新知