zoukankan      html  css  js  c++  java
  • 并发编程(进程、守护进程)

    一、操作系统的发展史:

      1:早期的计算工作方式,手工操作---穿孔卡片

    手工操作方式的两个特点:

      (1)用户独占全机。不会出现因资源已被其他用户占用而等待的现象,但资源的利用率低。
      (2)CPU 等待手工操作。CPU的利用不充分。
    为了提高手工操作引起的系统资源利用率,实现作业的自动过度,引出了批处理的概念

    二、进程理论

      程序:就是一堆代码

      进程,顾名思义就是正在执行的一个过程

      1:操作系统的作用:

        (1):隐藏丑陋复杂的硬件接口,提供良好的抽象接口

        (2):管理、调度进程,并且将多个进程对硬件的竞争变得有序

      2:多道技术:

        (1):产生背景:针对单核,实现并发

        (2):空间上的复用:如内存中同时有多道程序 ,多个程序共用一套计算机硬件 

        (3):时间上的复用:复用一个cpu的时间片,切换+保持状态(如洗衣服的等待同时可以去烧水、做饭等)

          1.当一个程序遇到IO操作 操作系统会剥夺该程序的cpu执行权限(提高了cpu的利用率 并且也不影响程序的执行效率)

          2..当一个程序长时间占用cpu 操作系统也会剥夺该程序的cpu执行权限(降低了程序的执行效率)

      3:进程调度:

        (1):先来先服务调度算法

        (2):短作业优先调度算法

        (3):时间片轮转法

        (4):多级反馈队列

      4:进程的并行与并发

        并行:并行是指两者同时进行,

        并发:看起来像是同时运行,如客户端在同时访问,时间的间隔很短

    单核的计算机不能实现并行,但是可以实现并发

    区别:

      并行:也就是在一个精确的时间片刻,有不同的程序在执行。并发:在一个时间端上可以看出是同时进行的

    多线程中的并发,并行与串行的区别:

    你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
    你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
    你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
    并发的关键是你有处理多个任务的能力,不一定要同时。
    并行的关键是你有同时处理多个任务的能力。
    所以我认为它们最关键的点就是:是否是『同时』。

    并发就是同步的串行,一个任务执行完执行下一个任务;
    并行,在用同一个时刻执行多个线程;
     

    三、同步异步阻塞非阻塞

    同步异步:表示的是任务的提交方式

       同步:任务提交之后,原地等待任务的执行并拿到返回结果才走,期间不做任何事(程序层面的表现就是卡住了)

       异步:任务提交之后,不再原地等待 而是继续执行下一行代码(结果是可以获取,但是用其他方式获取)>>> 结果的获取使用异步回调机制 

    阻塞非阻塞:表示的程序的运行状态

        阻塞:阻塞态
         非阻塞:就绪态或者运行态

      注意:强调:同步异步 阻塞非阻塞是两对概念 不能混为一谈

    在程序中的表示

    同步阻塞形式:

      效率最低:比如只专心的排队,什么事也不用管

    异步阻塞形式:排队的同时可以做别的事,等待通知在执行下去

    四、进程的创建与结束

     1: 创建进程的两种方式:

    from multiprocessing import Process
    import time
    
    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('')
    
    >>>
    主进程
    james is running 
    james is over 

    第二种是利用模块的内置方法继承

    # 创建进程的第二种方式 以继承Process类的形式开启进程的方式
    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('')
    
    >>>
    # 创建进程就是在内存重新开辟一块内存空间
    # 将允许产生的代码放进去
    # 一个进程对应在内存就是一块独立的内存空间
    # 进程与进程之间数据是隔离的 无法直接交互
    # 但是可以通过某些技术实现间接的交互

    2:join 方法 

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

    p.start() 仅仅只是告诉操作系统帮你创建一个进程,至于什么时候创,随机的.

    p.join() 主进程等待子进程完全结束 后才执行之后的代码 

    进阶,多个进程同时运行(注意,子进程的执行顺序不是根据启动顺序决定的)

     五、进程间的数据是隔离的

    # 进程之间的数据是隔离的
    import time
    from multiprocessing import Process
    
    money =100
    def index():
        global money
        money=90000000
    
    if __name__ == '__main__':
        p=Process(target=index)
        p.start()
        p.join()
        time.sleep(1) # 休眠1s 确保 子进程已经完全运行
        print(money)
        print('数据获取了全局的值吗?')
    
    >>>
    
    100
    数据获取了全局的值吗?

      通过代码执行的结果验证了子进程与主进程的数据是隔离的状态,不能访问到全局外的数据

    六、multiprocess模块,multtiprocess.process模块

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

    习惯看函数的内部封装原理,了解Process 内部的传参target:目标 对象  

    强调:
    1. 需要使用关键字的方式来指定参数
    2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号,即使所传的元素只有一个也要加,不然会报错。

    参数介绍:

    1 group参数未使用,值始终为None
    2 target表示调用对象,即子进程要执行的任务
    3 args表示调用对象的位置参数元组,args=(1,2,'egon',)
    4 kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
    5 name为子进程的名称

    方法介绍:

    1:p.start():启动进程,并调用该子进程中的p.run()

    2:p.join只能join住start开启的进程,而不能join住run开启的进程 

    3:p.terminate():强制终止进程p,不会进行任何清理操作

    4 p.is_alive():如果p仍然运行,返回True

    属性:

    1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置

    2 p.name:进程的名称

    3 p.pid:进程的pid

    在windows中使用process模块的注意事项

      创建子进程时必须放到if __name__ ==‘__main__ 后面

    七、多进程的其他方法

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

    例如:python是在pycharm里运行的  pycharm就是python的主主进程

    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.getpid())
        p_lst = []
        for i in range(5):
            p = Process(target=f, args=(i,))
            p.start()

    2: p.terminate() 强制终止当前进程,只是告知操作系统把进程结束,至于什么时候结束不一定,

      加个time.sleep(),让子进程完全结束掉

      p.is_alive()  判断进程是否存活,结果返回的是布尔值,False或True

    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()
        p.terminate()  # 杀死当前进程  其实是告诉操作系统帮你杀死一个进程
        time.sleep(0.1)
        print(p.is_alive())  # 判断进程是否存活
        # print('主',current_process().pid)
        print('',os.getpid(),'主主进程:%s'%os.getppid())

     3:守护进程:p.daemon

      会随着主进程的结束而结束

    主进程创建守护进程:

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

      2:守护进行内无法在开启子进程,否者抛出异常:AssertionError:

    进程自己是互相独立的,主进程代码运行结束,守护进程随即终止

    from multiprocessing import Process
    import  time
    def test(name):
        print("%s老二还活着"%name)
        time.sleep(2)
        print("%s老弟正常死亡"%name)
    
    if __name__ == '__main__':
        p=Process(target=test,args=('james',))
        p.daemon=True
        p.start()
        time.sleep(0.1)  #守护进程时间过短执行完第一句就直接回主进程打印
        print("老大奄奄一息!")
    
    >>
    james老二还活着
    老大奄奄一息!

    七、僵尸进程与孤儿进程

      僵尸进程: 一个父进程利用fork创建子进程,如果子进程退出,而父进程没有利用wait 或者  waitpid 来获取子进程的状态信息,那么子进程的状态描述符依然保存在系统中。

    父进程回收子进程资源的两种方式
    1.join方法
    2.父进程正常死亡
    所有的进程都会步入僵尸进程


    孤儿进程:一个父进程退出, 而它的一个或几个子进程仍然还在运行,那么这些子进程就会变成孤儿进程,孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集的工作

    孤儿进程
    子进程没死 父进程意外死亡

    针对linux会有儿童福利院(init) 如果父进程意外死亡他所创建的子进程都会被福利院收


    互斥锁
    当多个进程操作同一份数据的时候 会造成数据的错乱, 这个时候必须加锁处理,例如抢票,在只有一张票的情况不加锁的时候,都显示抢票成功
    将并发变成串行
    虽然降低了效率但是提高了数据的安全
    注意:
    1.锁不要轻易使用 容易造成死锁现象
    2.只在处理数据的部分加锁 不要在全局加锁

    锁必须在主进程中产生 交给子进程去使用

    from multiprocessing import Process,Lock  # 模块 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()

     结果:加锁后,当有一个抢票成功后,后面的已经没有了,数据

    :

     

  • 相关阅读:
    初级程序员面试不靠谱指南(三)
    [细节决定B度]之回首一瞥cout<<"Hello,world"<<endl;
    20110310日记wmh
    SliverLight 控件属性的赋值方式
    20110309wmh日记
    20110308wmh日记
    20110307wmh日记
    STOAdiary20110314完成的任务
    20110313wmh日记
    20110311wmh日记
  • 原文地址:https://www.cnblogs.com/Gaimo/p/11328004.html
Copyright © 2011-2022 走看看