zoukankan      html  css  js  c++  java
  • 并发编程之多进程

    一:什么是进程?

      正在进行的一个过程或者说一个任务。而负责执行任务的是cpu。进程的概念起源于操作系统,是操作系统最核心的概念,操作系统的其他所有的内容都是围绕进程的概念展开的。

    什么是操作系统?
        操作系统就是一个协调、管理和控制计算机硬件资源和软件资源的控制程序
    (操作系统由操作系统的内核(运行内核态,管理硬件资源)以及
    系统调用(运行于用户态,为应用程序员提供系统调用接口)两部分组成,
    所以,单纯的说操作系统运行于内核态是不准确的

    下图是操作系统在整个计算机中的位置:

    操作系统的功能1.隐藏丑陋复杂的硬件接口,提供良好的抽象接口
        2.管理、调度进程,并且将多个进程对硬件的竞争变得有序 
    操作系统的作用:
      1.为应用程序提供如何使用硬件资源的抽象概念
      2.管理硬件资源
    Grace在一个时间都拿要做好多任务:上课,去见张艺兴,洗衣服,但是同一个时刻只能做一个任务(CPU同一时间只能做一个事情),如何才能玩出多个任务并发执行的效果?
        Grace先去上课,再去见张艺兴,然后再回去洗衣服。。。就保证每个任务都在进行中
    单核+多道,实现多个进程的并发执行

    二:进程与程序的区别

      程序仅仅是一堆代码而已,进程指的是程序的运行过程。

      需要强调的是:同一个程序执行两次,那也是两个进程,比如打开qq,虽然都是同一个软件,但是一个可以登录Grace,一个可以让张艺兴登录。

    三:多道技术

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

      空间上的复用:将内存分为几部分,每个部分放入一个程序,这样,同一时间内存中就有了多道程序。

      时间上的复用(复用一个CPU的时间片):当一个程序在等待I/O时,另一个程序可以使用CPU,如果内存中可以同时存放足够多的作业,则CPU利用率可以达到100%。(操作系统采用了多道技术之后,可以控制进程的切换,或者说进程之间去争抢CPU的执行权限。这种切换不仅会在一个进程遇到I/O时进行,一个进程占用CPU的时间过长也会切换,或者说被操作系统夺走CPU的执行权限)核心在于切之前将进程的状态保存下来,这样才能保证下次切换回来时,能基于上次切走的位置继续运行。

      空间复用的最大的问题是:程序之间的内存必须分割,这种分割需要在硬件层面实现,由操作系统控制,如果内存彼此不分割,则一个程序可以访问另外一个程序的内存。

      首先丧失安全性:如果你的qq程序可以访问操作系统的内存,这意味着qq可以拿到操作系统的所有权限。

      其次丧失稳定性:某个程序崩溃时可能吧别的程序的内存也回收了,比如说吧操作系统的内存给回收了,则操作系统崩溃。

     四:并发与并行

      无论是并发还是并行,在用户看来都是‘同时’,不管是进程还是线程,都只是一个任务而已,真正干活的是CPU,CPU来做这些任务,而一个CPU同一时刻只能执行一个任务。

      并发:并发是伪并行,即看起来是同时运行。单个CPU+多道技术就可以实现并发(并行也属于并发)

      并行:同时运行,只有具备多个CPU才能实现并行

      单核下,可以利用多道技术,多个核,每个核也都可以利用多道技术(多道技术是针对单核而言的)

     五:阻塞与非阻塞

      阻塞状态是因为程序遇到了I/O操作,或者sleep,导致后续代码不能被CPU执行

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

    进程有三种状态:

        就绪态、运行态、阻塞态

    多道技术会在进程执行时间过长或遇到I/O时自动切换其他进程,以为着I/O操作与进程被剥夺CPU执行权都会造成进程阻塞。

    六:进程的创建

      对于通用系统(跑很多应用程序),需要有系统运行过程中创建或撤销进程的能力,主要分为四种形式创建新的进程。

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

       2.一个进程在运行过程中开启了子进程(如nginx开启多进程)

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

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

     无论哪一种,新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的。

    七:进程

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

    方式一:实例化Process类

    from multiprocessing import Process
    import os
    
    def task():
        print('self',os.getpid())    #task函数
        print('parent',os.getppid())    #当前py文件
        print('task run')
    
    # windows创建子进程是,子进程会将父进程的代码加载一遍,导致重复创建子进程
    # 所以一定要将创建子进程的代码放在main的下面
    if __name__ == '__main__':
        print('self',os.getpid())   #pycharm解释器
        print('parent',os.getppid())   #当前py 文件
        p=Process(target=task,name='子进程')   # 创建一个表示进程的对象,但并不是创建一个进程
        p.start()
    方式一

    方式二:继承Process类,并覆盖run方法

    # 创建进程的第二种方式,继承Process 覆盖run方法,在子进程启动以后自动执行run方法
    # 在子进程启动以后会自动执行run方法
    # 其优势是 可以自定义 进程的属性和行为 来完成一些额外任务 例如下载
    
    class MyProcess(Process):
        def __init__(self,url):
            self.url=url
            super().__init__()
        # 子类中的方法只有run会自动执行,也就是说只能叫run
        def run(self):
            print('self', os.getpid())
            print('parent', os.getppid())
            print('下载文件',self.url)
            print('run')
    
    if __name__ == '__main__':
        print('self',os.getpid())
        print('parent',os.getppid())
        p=MyProcess('www.baidu.com/xx.MP4')
        p.start()
    方式二

    需要注意的是:在windows下,开启子进程必须放到__main__下面,因为windows在开启子进程时会重新加载所有的代码造成递归。

      7.2进程间内存相互独立

    from multiprocessing import Process
    
    import time
    a = 1000000000000
    def task():
        global a
        print(id(a))
        a = 0
        print("子进程的",a)
    
    if __name__ == '__main__':
        print(id(a))
        p = Process(target=task)
        p.start() # 给操作系统发送请求后 代码继续往下运行 至于子进程 什么时候创建 什么是执行 都跟当前进程没关系
    
        time.sleep(1)
        print("自己的",a)
    
    # 子进程中的数据修改 不会影响父进程
    进程间内存相互独立

      7.3join函数

    join函数可以让父进程等待子进程结束后继续执行(本质上,提高了子进程的优先级,当CPU在切换时,会优先执行子进程)

    import time
    from multiprocessing import Process
    def task():
        print("上传文件....")
        time.sleep(3)
        print("上传结束...")
    
    
    # [进程1,进程2,进程3]
    
    if __name__ == '__main__':
        p = Process(target=task)
        p.start()
    
        p.join() # 本质上  是提高了子进程优先级   当CPU在切换时 会优先切子进程
    
        print("上传文件成功!")
    父进程等待子进程结束
    import time
    from multiprocessing import Process
    def task(num):
        print("我是%s号 进程" % num)
        time.sleep(2)
        print("=========")
    
    if __name__ == '__main__':
        start_time = time.time()
        ps = []
        for i in range(3):
            p = Process(target=task,args=(i,))
            p.start()
            print("----")
            # ps.append(p)
    
        for p in ps:
            p.join()
    
        print(time.time()-start_time)
        print("over")
    案例二

      7.4 process常用属性

    from multiprocessing import  Process
    
    import  time
    def task():
        time.sleep(3)
        print("执行完毕!")
    
    
    if __name__ == '__main__':
    
        p = Process(target=task,name="alex")
        p.start()
        print(p.name)
    
        # time.sleep(1)
        # print(p.is_alive())
        #
        # p.terminate()
        #
        # print(p.is_alive())   #判断是否活着,值是布尔值
    
        p.terminate()# 终止这个进程
        print(p.pid)
        p.daemon  # 守护进程
  • 相关阅读:
    mysql启动错误
    maven环境变量配置
    记一次服务器Tomcat优化经历
    自动定时备份删除脚本
    Tomcat网页加载速度过慢的解决方法
    tomcat运行war包报错,找不到context-root文件
    maven下配置pom.xml
    [LeetCode]题解(python):116-Populating Next Right Pointers in Each Node
    [LeetCode]题解(python):115-Distinct Subsequences
    [LeetCode]题解(python):114-Flatten Binary Tree to Linked List
  • 原文地址:https://www.cnblogs.com/liuxiaolu/p/10191827.html
Copyright © 2011-2022 走看看