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()

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

    :

     

  • 相关阅读:
    C++学习9 this指针详解
    福建省第八届 Triangles
    UVA 11584 Partitioning by Palindromes
    POJ 2752 Seek the Name, Seek the Fame
    UVA 11437 Triangle Fun
    UVA 11488 Hyper Prefix Sets (字典树)
    HDU 2988 Dark roads(kruskal模板题)
    HDU 1385 Minimum Transport Cost
    HDU 2112 HDU Today
    HDU 1548 A strange lift(最短路&&bfs)
  • 原文地址:https://www.cnblogs.com/Gaimo/p/11328004.html
Copyright © 2011-2022 走看看