• 并发编程知识的简单整理(一)


    20190914

    并发编程知识的整理

    操作的系统的历史介绍

    第一代计算机(1940~1955):真空管和插件板

    第二代计算机(1955~1965):晶体管和批处理系统

    第三代计算机(1965~1980):集成电路芯片和多道程序设计

    **多道技术 **:多道技术中的多道指的是多个程序,多道技术的实现是为了解决多个程序竞争或者说共享同一个资源(比如cpu)的有序调度问题,解决方式即多路复用,多路复用分为时间上的复用和空间上的复用。

    空间上的复用:将内存分为几部分,每个部分放入一个程序,这样,同一时间内存中就有了多道程序(共用一个内存条,多个进程互相隔离,物理级别隔离,程序与程序之间互不干扰

    时间上的复用:当一个程序在等待I/O时,另一个程序可以使用cpu,如果内存中可以同时存放足够多的作业,则cpu的利用率可以接近100%,类似于我们小学数学所学的统筹方法。(操作系统采用了多道技术后,可以控制进程的切换,或者说进程之间去争抢cpu的执行权限。这种切换不仅会在一个进程遇到 io 时进行,一个进程占用cpu时间过长也会切换,或者说被操作系统夺走cpu的执行权限)共用一个cpu

    分时操作系统:
    多个联机终端+多道技术

    串行:一个程序完完整整的执行完才能运行下一个程序

    并发:看起来像在同时运行(单核),实际上也是串行,区别在于它不一定执行完整,中途在不停地切换

    并行:同时执行多个程序

    程序:躺在硬盘上的文件
    进程:一个执行的程序,是一系列资源的总和

    进程与程序区别:进程是正在进行的一个过程或者说一个任务,而程序仅仅仅只是一堆代码。

    需要强调的是:同一个程序执行两次,那也是两个进程,比如打开暴风影音,虽然都是同一个软件,但是一个可以播放苍井空,一个可以播放饭岛爱。

    进程的三个基本状态

    进程执行时的间断性,决定了进程可能具有多种状态。事实上,运行中的进程可能具有以下三种基本状态。
    就绪状态(Ready):

    进程已获得除处理器外的所需资源,等待分配处理器资源;只要分配了处理器进程就可执行。就绪进程可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。
    运行状态(Running):
    进程占用处理器资源;处于此状态的进程的数目小于等于处理器的数目。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。
    阻塞状态(Blocked):
    由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,也无法运行

    尽量减少阻塞状态可以提升我们程序运行的效率

    并发 = 保存状态+切换

    开启子进程 申请新的内存空间 把父进程的所有代码完整拷贝一份过去

    # from multiprocessing import Process
    # def task(x):
    #     pass
    #
    # if __name__ == '__main__':
    #     p = Process(target=task,args=(45,))
    #     p.start()
    

    第二种开启子进程的方式(不常用)

    # from multiprocessing import Process
    #
    #
    # class Xx(Process):
    #     def __init__(self,x):
    #         super().__init__()
    #         self.x = x
    #     def run(self):
    #         pass
    #
    #
    # p = Xx()
    # p.start()
    

    僵尸进程:父进程的子进程结束的时候父进程没有wait()情况下子进程会变成僵尸进程。(父进程一直不死不停造子进程并且不回收僵尸进程有害)

    父进程等着所有的子进程结束才会结束。

    孤儿进程(无害)

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

    情况1 无害
    父进等着子进程都死,回收僵尸进程。

    情况2 无害
    父进程死了,子进程活着,都要被init进程接管并且回收。

    情况3 有害
    父进程一直不死,造成了大量僵尸进程。占用了大量的pid号

    pid号是有限的。
    解决方案:
    最直接的办法就是杀死父进程 。

    20190915

    Process中join方法

    join:阻塞住主进程再等待子进程结束,然后再往下执行,

    具体代码如下:

    from multiprocessing import Process
    import time
    def foo():
        time.sleep(4)
        print('进程  start ')
        time.sleep(5)
        print('进程  end ')
    
    
    
    if __name__ == '__main__':
        p = Process(target=foo)
        p.start() #
        p.join(3) # 阻塞住主进程再等待子进程结束,然后再往下执行,(了解的是:内部会待用wait())
        print('主')
    

    p.join(3):括号内的数字代表阻塞的时间

    多个join的情况,代码如下

    from multiprocessing import Process
    import time
    def task(n):
        print('子进程 开始')
        time.sleep(n)
        print('子进程 结束')
    if __name__ == '__main__':
        start_time = time.time() # 记录开始时间
        p1 = Process(target=task,args=(2,))
        p2 = Process(target=task,args=(4,))
        p3 = Process(target=task,args=(6,))
    
        p1.start()
        p2.start()
        p3.start()
        # 换顺序也是一样的也是按照时长最长的那个计算。
        p1.join() # 等待2s
        p2.join() # 等待2s
        p3.join() # 等待2s
        end_time = time.time() # 记录结束时间
        print(end_time-start_time) # 计算时间差
        print('主线程')
        
        
        
        子进程 开始
        子进程 开始
        子进程 开始
        子进程 结束
        子进程 结束
        子进程 结束
        7.566513776779175
        主线程
    

    join串行的情况

    from multiprocessing import Process
    import time
    def task(n):
        print('子进程 开始')
        time.sleep(n)
        print('子进程 结束')
    if __name__ == '__main__':
        start_time = time.time() # 记录开始时间
        p1 = Process(target=task,args=(2,))
        p2 = Process(target=task,args=(4,))
        p3 = Process(target=task,args=(6,))
    
        p1.start()
        p1.join() 
        p2.start()
        p2.join() 
        p3.start()
        p3.join() 
        end_time = time.time() # 记录结束时间
        print(end_time-start_time) # 计算时间差
        print('主线程')
    
        '''输出:
        
        子进程 开始
        子进程 结束
        子进程 开始
        子进程 结束
        子进程 开始
        子进程 结束
        15.407774925231934
        主线程
        
        '''
        # ps:反而不如不开进程,正常调用三次来的快。
    

    精炼代码:

    from multiprocessing import Process
    import time
    def task(n):
        print('子进程 开始')
        time.sleep(n)
        print('子进程 结束')
    if __name__ == '__main__':
        start_time = time.time() # 记录开始时间
        task_list = []
        for i in range(1,4):
            p = Process(target=task,args=(i,))
            p.start()
            task_list.append(p)
        print(task_list) # [<Process(Process-1, started)>, <Process(Process-2, started)>, <Process(Process-3, started)>]
        for i in task_list:
            i.join()
        end_time = time.time() # 记录结束时间
        print(end_time-start_time) # 计算时间差 4.764175891876221
        print('主线程')
    

    pid的用法

    在当前进程查看当前进程pid
        os.getpid()
        current_process().pid
    在当前进程查看子进程pid
        子进程对象.pid()
    在当前进程查看父进程pid
        os.getppid() 
        
        
    from multiprocessing import Process,current_process
    import time,os
    
    def task():
    
        print('子进程 start')
        print('在子进程中查看自己的pid',current_process().pid) # 在子进程中查看自己的pid
        print('在子进程中查看父进程的pid',os.getppid()) #
        time.sleep(200)
        print('子进程 end')
    
    if __name__ == '__main__':
    
        p = Process(target=task)
        p.start()
        print('在主进程查看子进程的pid',p.pid) # 一定要写在 start()之后
        print('主进程的pid',os.getpid())
        print('主进程的父进程pid',os.getppid())
        print('主')
    

    name的用法:

    from multiprocessing import Process,current_process
    
    def task():
        print(current_process().name) # 在子进程查看自己的name
    
    if __name__ == '__main__':
        p = Process(target=task) 
        p2 = Process(target=task)
        p3 = Process(target=task,name='rocky') # 已定义name属性为rocky
        p.start()
        p2.start()
        p3.start()
        print(p.name) #Process-1
        print(p2.name) #Process-2
        print(p3.name) #rocky
    

    is_alive用法:

    from multiprocessing import Process,current_process
    import time
    def task():
        print('子进程开始')
        time.sleep(1)
        print('子进程结束')
    
    if __name__ == '__main__':
        p = Process(target=task)
        p.start()
        # p.terminate() #发送一个指令给操作系统但是不会立即结束子进程
        # time.sleep(1)
        print(p.is_alive()) # True 判断子进程代码是否结束
        time.sleep(3)
        print(p.is_alive()) # False
    

    守护进程:

    主进程创建守护进程

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

      其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

    什么情况适合用守护进程?

    当主进程代码结束,该子进程再执行无意义的情况可以用守护进程。

    具体代码如下

    例一:主进程代码运行完,守护进程立即结束

    from multiprocessing import Process
    import time
    def foo():
        print('守护进程开始')
        time.sleep(5)
        print('守护进程开始')
    
    
    if __name__ == '__main__':
        p1 = Process(target=foo)
        p1.daemon = True # 一定要凡在start之前,表示设置为一个守护进程。
        p1.start()
        print('主进程')
        
        '''输出:
        主进程
        '''
        # 守护进程一旦发现主进程代码运行完,立刻结束,并不会管自己的进程是否运行完
    

    例二 主进程代码运行完后等在子进程运行阶段,线程不会参与守护

    from multiprocessing import Process
    
    import time
    def foo():
        print('守护进程开始')
        time.sleep(5)
        print('守护进程结束')
    def task():
        print('子进程开始')
        time.sleep(3)
        print('子进程开始')
    
    if __name__ == '__main__':
        p1 = Process(target=foo)
        p2 = Process(target=task)
        p1.daemon = True # 一定要凡在start 之前
        p1.start()
        p2.start()
        # p2 = Process(target=task)
        print('主进程')
    
        '''输出:
        主进程
        子进程开始
        子进程开始
        '''
        #分析 守护进程在运行完主进程最后一行代码就结束,但是主进程并没有结束,主进程在等待子进程运行结束.
    
  • 相关阅读:
    dtclog
    求助解决 SQL SERVER 2005 log 事务日志增长太快的问题
    开辟第二战场
    c# 排序 求助
    怎样玩转3D
    爬楼梯问题迭代算法解!
    C++中类的继承方式的区别以及private public protected 范围
    想转c++
    PHP相关笔记
    常用快捷键
  • 原文地址:https://www.cnblogs.com/asyouwish/p/11522437.html
Copyright © 2020-2023  润新知