zoukankan      html  css  js  c++  java
  • 进程的介绍和使用

    一、进程

    1.进程:顾名思义,进程即正在执行的一个程序的过程

    想要真正的了解进程,必须事先了解操作系统,需要具备的一些理论基础:

    操作系统:

    1.提供软硬件资源

    2.管理、调度进程

    多道技术:
       1.产生背景:针对单核,实现并发
        2.空间上的复用
            多个程序共用一套计算机硬件
        
        3.时间上的复用
            切换+保存状态
                1.当一个程序遇到IO操作 操作系统会剥夺该程序的cpu执行权限(提高了cpu的利用率 并且也不影响程序的执行效率)
                2.当一个程序长时间占用cpu 操作系统也会剥夺该程序的cpu执行权限(降低了程序的执行效率)
                
            并发:看起来像同时运行的就可以
            并行:真正意义上的同时执行
            单核的计算机能不能实现并行,但是可以实现并发

    进程是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

    注意:同一个程序执行两次,就会在操作系统中出现两个进程,所以我们可以同时运行一个软件,分别做不同的事情也不会混乱。一个进程对应到内存中就是一块独立的内存空间。与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内

    2.进程的并发和并行

    并发:看起来像同时在运行  (单核+多道技术就可以实现并发)

    并行:真正的同时在运行

    多进程是实现并发的手段之一(******)

    3.同步异步阻塞非阻塞   (能进入阻塞状态的原因有:输入,输出,文件操作(IO),sleep)

    同步异步:表示的是任务的提交方式
            同步:任务提交之后 原地等待的任务的执行并拿到返回结果才走 期间不做任何事(程序层面的表现就是卡住了)
            异步:任务提交之后 不再原地等待 而是继续执行下一行代码(结果是要的  但是是用过其他方式获取)
     
    阻塞非阻塞:表示的程序的运行状态
            阻塞:阻塞态
            非阻塞:就绪态 运行态

    异步非阻塞形式的效率最高

    注意:同步异步  阻塞非阻塞是两队概念  不能混为一谈(******)

    小结:

    #1. 同步与异步针对的是函数/任务的调用方式:同步就是当一个进程发起一个函数(任务)调用的时候,一直等到函数(任务)完成,而进程继续处于激活状态。而异步情况下是当一个进程发起一个函数(任务)调用的时候,不会等函数返回,而是继续往下执行当,函数返回的时候通过状态、通知、事件等方式通知进程任务完成。
    
    #2. 阻塞与非阻塞针对的是进程或线程:阻塞是当请求不能满足的时候就将进程挂起,而非阻塞则不会阻塞当前进程

     二、创建进程的两种方式(******)

     第一种创建方式:(一般都是使用第一种)        创建一个子进程,会把前面的代码从上往下再走一遍(******)

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

    1 p.start():启动进程,并调用该子进程中的p.run()   (******2 p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法   (自定义类中会使用到重写run方法)
    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开启的进程    (主进程等待子进程结束再结束)
    Process模块方法介绍
    1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
    2 p.name:进程的名称
    3 p.pid:进程的pid
    Process模块属性介绍

    执行顺序:先执行主进程,在执行子进程

    from multiprocessing import Process

    def test(name): print('%s is running'%name) time.sleep(3) print('%s is over'%name) """ windows创建进程会将代码以模块的方式 从上往下执行一遍 linux会直接将代码完完整整的拷贝一份 windows创建进程一定要在if __name__ == '__main__':代码块内创建 否则报错 """ if __name__ == '__main__': p = Process(target=test,args=('egon',)) # 创建一个进程对象 p.start() # 仅仅是告诉操作系统帮你创建一个进程 print('') #主进程

    #结果

    egon is running
    egon is over
    在Windows操作系统中由于没有fork(linux操作系统中创建进程的机制),在创建子进程的时候会自动 import 启动它的这个文件,而在 import 的时候又执行了整个文件。因此如果将process()直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if __name__ ==‘__main__’ 判断保护起来,import 的时候  ,就不会递归运行了。
    在windows系统中使用需要process模块注意事项
    创建进程就是在内存中重新开辟一块内存空间,将允许产生的代码丢进去,一个进程对应在内存就是一块独立的内存空间
    
    进程与进程之间数据是隔离的 无法直接交互,但是可以通过某些技术实现间接交互。

    第二种创建方式   (定义一个类,然后继承Process,重写run方法,创建的时候使用自己定义的类)

    from multiprocessing import Process
    import time
    
    class MyProcess(Process):
        def __init__(self,name):
            super().__init__()
            self.name = name
    
        def run(self):
            print('%s is running' % self.name)
            time.sleep(3)
            print('%s is over' % self.name)
    
    if __name__ == '__main__':
        p = MyProcess('egon')
        p.start()
        print('')

    注意:多个进程同时运行,子进程的执行顺序不是根据启动顺序决定的,随机分配哪个先执行。

    三、join方法

     join方法是让子进程先运行,等待子进程运行结束再运行主进程。

    from multiprocessing import Process
    import time
    
    def test(name,i):
        print('%s is running'%name)
        time.sleep(i)
        print('%s is over'%name)
    if __name__ == '__main__':
        p = Process(target=test,args=('egon',1))
        p1 = Process(target=test,args=('kevin',2))
        p2 = Process(target=test,args=('jason',3))
        start_time = time.time()
        p.start()  # 仅仅是告诉操作系统帮你创建一个进程 至于这个进程什么时候创  操作系统随机决定
        p1.start()
        p2.start()
        p2.join()
        p.join()
        p1.join()     # 主进程代码等待子进程运行结束 才继续运行
        print('')
        print(time.time() - start_time)

    使用join方法,先运行子进程,子进程运行也是随机分配的,子进程运行完毕之后到主进程开始运行。

    egon is running
    kevin is running
    jason is running
    egon is over
    kevin is over
    jason is over
    主
    4.229077339172363
    运行结果

    四、验证进程间数据是隔离的

     因为主进程和子进程是两个独立的内存空间,无法直接交互,所以这个还是取得主进程的money值100

    from multiprocessing import Process
    
    money = 100
    def test(): global money money = 99999999 if __name__ == '__main__': p = Process(target=test) p.start() p.join() print(money) #访问全局变量的money #结果 100

    五、进程对象的及其他方法

     current_process模块   current_process().pid 获取进程号     也可以用os.getpid()表示子进程的进程号,os.getppid()表示父类的进程号

    from multiprocessing import Process,current_process
    import os
    import time
    
    def test(name):
        print('%s is running'%name,current_process().pid)
        print('%s is running'%name,'子进程%s'%os.getpid(),'父进程%s'%os.getppid())
        time.sleep(3)
        print('%s is over'%name)
    
    
    if __name__ == '__main__':
        p = Process(target=test,args=('egon',))
        p.start()
        print('',os.getpid(),'主主进程:%s'%os.getppid())
    
    #结果
    主 12248 主主进程:8372
    egon is running 4116
    egon is running 子进程4116 父进程12248
    egon is over

    terminate()    告诉操作系统帮你杀死当前进程,如果没有给操作系统反应时间会直接跳过这条。如果杀死了,就不会执行这个进程的内容了。

    from multiprocessing import Process
    import os
    import time
    
    def test(name):
        print('%s is running'%name,'子进程%s'%os.getpid(),'父进程%s'%os.getppid())
        time.sleep(3)
        print('%s is over'%name)
    
    if __name__ == '__main__':
        p = Process(target=test,args=('egon',))
        p.start()
        p.terminate()  # 杀死当前进程  其实是告诉操作系统帮你杀死一个进程
        time.sleep(0.1)
        print(p.is_alive())  # 判断进程是否存活
        print('',os.getpid(),'主主进程:%s'%os.getppid())
    
    #结果
    False
    主 15080 主主进程:8372

    六、守护进程

    注意:进程之间是相互独立的,守护进程会随着主进程的结束而结束。

    主进程创建守护进程

      其一:守护进程会在主进程代码执行结束后就终止

      其二:守护进程内无法再开启子进程,否则抛出异常

    from multiprocessing import Process
    import time
    
    def test(name):
        print('%s总管正常活着'%name)
        time.sleep(3)
        print('%s总管正常死亡'%name)
    
    if __name__ == '__main__':
        p = Process(target=test,args=('egon',))
        p.daemon = True  # 将该进程设置为守护进程   这一句话必须放在start语句之前 否则报错
        p.start()        # 开启子进程
        time.sleep(0.1)
        print('皇帝jason寿正终寝')
    
    #结果
    皇帝jason寿正终寝

    七、互斥锁(******)

     当多个进程使用同一份数据资源的时候,就会引发数据安全或顺序混乱的问题。由并发变成了串行,牺牲了运行效率,但保证了数据的安全性。

    导入模块Lock,并不建议使用很多的锁,因为会很限制效率,只在修改数据的地方添加锁。

    from multiprocessing import Process,Lock
    import time
    import json
    
    # 查票
    def search(i):
        with open('data','r',encoding='utf-8') as f:
            data = f.read()
        t_d = json.loads(data)
        print('用户%s查询余票为:%s'%(i,t_d.get('ticket')))
    
    # 买票
    def buy(i):
        with open('data','r',encoding='utf-8') as f:
            data = f.read()
        t_d = json.loads(data)
        time.sleep(1)
        if t_d.get('ticket') > 0:
            # 票数减一
            t_d['ticket'] -= 1
            # 更新票数
            with open('data','w',encoding='utf-8') as f:
                json.dump(t_d,f)
            print('用户%s抢票成功'%i)
        else:
            print('没票了')
    
    def run(i,mutex):
        search(i)
        mutex.acquire()  # 抢锁  只要有人抢到了锁 其他人必须等待该人释放锁
        buy(i)
        mutex.release()  # 释放锁
    
    if __name__ == '__main__':
        mutex = Lock()  # 生成了一把锁
        for i in range(10):
            p = Process(target=run,args=(i,mutex))
            p.start()

    注意:

    1.锁不要轻易使用,容易造成死锁现象

    2.只在处理数据的部分加锁,不要在全局加锁 

    锁必须在主进程中产生,交给子进程

  • 相关阅读:
    Mac开发利器之程序员编辑器MacVim学习总结(转)
    FFmpeg的H.264解码器源代码简单分析
    Google推荐的图片加载库Glide
    Java集合及concurrent并发包总结(转)
    ArrayList、Vector和LinkedList等的差别与用法(基础回顾)
    应用最广泛的模式-工厂方法模式
    Android 开发 命名规范(基础回顾)
    Java线程问题(基础回顾)
    Mac下使用wireshark解决Interface为空的办法
    Express4.10.2开发框架中默认app.js的代码注释
  • 原文地址:https://www.cnblogs.com/wangcuican/p/11329342.html
Copyright © 2011-2022 走看看