Python-Level2-day12:多任务概述,进程概述,多进程编程,进程处理细节(僵尸&孤儿进程,进程函数编写风格),面向对象创建进程类

前情回顾
​
1.  TCP数据传输特点
​
     *  面向连接的传输服务:传输之前必须建立起连接关系
     *  可靠的数据传输
     *  三次握手和四次挥手能描述
     *  tcp首部信息
     *  字节流方式传输:没有消息边界
     *  数据传输过程 短连接与长连接形态
​
2.  tcp套接字编程
​
      服务端 : socket->bind->listen->accept(阻塞1)->recv/send(阻塞2)->close
      客户端 : socket--------------->connect->send/recv->close
注意:listen只是表示具备被连接的能力,还要看后面的accept
此外,监听队列大小不代表可以连接多少客户端,表示可以同时的处理几个
客户端发起连接。
​
3. tcp传输细节
​
     recv它什么时候返回空字节串:突然断开或close断开
     send如果对方不存在还发送:管道爆裂报错
     tcp套接字缓冲区:可以协调收发速度
     粘包问题:发得快收的慢  没有消息边界
​
4. tcp与udp对比 (可以描述)
​
​
cookie :了解
在win 下创建进程 (避免子进程再创建子进程...)这样写:
​
    if  __name__ == "__main__":#顶层模块才运行以下程序
       p = Process(target=fun)#功能模块在其他模块运行就不执行以下程序
       p.start()
​
​
练习01: 文件拆分
假设有一个大文件 ,将这个文件拆分为2个小文件
按照字节平均分即可.要去两个小文件的拆分过程
同时执行,以提高效率.
​
提示: 获取文件大小  os.path.getsize()
​
plus :  循环读取复制内容
​
​
作业 :
1. 能够熟练创建进程
2. 打印100000以内质数,将100000分成4份,创建
4个子进程,每个求其中一份,最后所有子执行完为止.
统计一共用了多少时间
​
​
​

2. 多任务编程

2.1 多任务概述

  • 多任务

    即操作系统中可以同时运行多个任务。比如我们可以同时挂着qq,听音乐,同时上网浏览网页。这是我们看得到的任务,在系统中还有很多系统任务在执行,现在的操作系统基本都是多任务操作系统,具备运行多任务的能力。

编辑

 

  • 计算机原理

    • CPU:计算机硬件的核心部件,用于对任务进行执行运算。

    • 操作系统调用CPU执行任务

       

    • cpu轮询机制 : cpu都在多个任务之间快速的切换执行,切换速度在微秒级别,其实cpu同时只执行一个任务,但是因为切换太快了,从应用层看好像所有任务同时在执行。

    • 多核CPU:现在的计算机一般都是多核CPU,比如四核,八核,我们可以理解为由多个单核CPU的集合。这时候在执行任务时就有了选择,可以将多个任务分配给某一个cpu核心,也可以将多个任务分配给多个cpu核心,操作系统会自动根据任务的复杂程度选择最优的分配方案。

      • 并发 : 多个任务如果被分配给了一个cpu内核,那么这多个任务之间就是并发关系,并发关系的多个任务之间并不是真正的"同时"。

      • 并行 : 多个任务如果被分配给了不同的cpu内核,那么这多个任务之间执行时就是并行关系,并行关系的多个任务时真正的“同时”执行。

  • 什么是多任务编程

    多任务编程即一个程序中编写多个任务,在程序运行时让这多个任务一起(并发或并行:操作系统决定,应用层看来都是并行)运行,而不是一个一个的顺次执行。比如微信视频聊天,这时候在微信运行过程中既用到了视频任务也用到了音频任务,甚至同时还能发消息。这就是典型的多任务。而实际的开发过程中这样的情况比比皆是。

    • 实现多任务编程的方法 : 多进程编程,多线程编程

  • 多任务意义

    • 提高了任务之间的配合,可以根据运行情况进行任务创建。

      比如: 你也不知道用户在微信使用中是否会进行视频聊天,总不能提前启动起来吧,这是需要根据用户的行为启动新任务。

    • 充分利用计算机资源,提高了任务的执行效率。

      • 在任务中无阻塞时只有并行状态才能提高效率,阻塞是占用资源的。

       

      • 在任务中有阻塞时并行并发都能提高效率,即利用任务的阻塞时间可以进行其他任务。因为阻塞不占cpu

       

2.2 进程(Process)

2.2.1 进程概述

  • 定义: 程序在计算机中的一次执行过程。

    • 程序是一个可执行的文件,是静态的占有磁盘。

    • 进程是一个动态的过程描述,占有计算机运行资源,有一定的生命周期。是动态的。再启动相同程序就开启一个新的进程。

  • 进程状态

    • 三态

      就绪态 : 进程具备执行条件,等待系统调度分配cpu资源

      运行态 : 进程占有cpu正在运行

      等待态 : 进程阻塞等待,此时会让出cpu,不占用cpu资源。

      就绪与执行态之间的时间片表示轮循,计算机一启动就有很多程序在阻塞态,例如语音助手等程序,只有当用这个程序的时候才会进入就绪态,然后执行。

       

    • 五态 (在三态基础上增加新建和终止)

      新建 : 创建一个进程,获取资源的过程

      终止 : 进程结束,释放资源的过程

       

  • 进程命令

    • 查看进程信息

      • ps -aux

      •  

        • USER : 进程的创建者

        • PID进程编号 : 操作系统分配给进程的编号,大于0的整数,系统中每个进程的PID都不重复。PID也是重要的识别区分进程的标志。

        • %CPU,%MEM : 占有的CPU和内存大小

        • STAT : 进程状态信息,S,I 表示阻塞状态 ,R 表示就绪状态或者运行状态,快速切换区分不开就绪与运行

        • START : 进程启动时间

        • COMMAND : 通过什么程序启动的进程

        •  

    • 进程树形结构

      pstree
      • 父子进程:在Linux操作系统中,进程形成树形关系,任务上一级进程是下一级的父进程,下一级进程是上一级的子进程。linux认为子进程是由父进程创建与发起的。

2.2.2 多进程编程

  • 使用标准库模块 : multiprocessing

  • 创建流程

    【1】 将需要新进程执行的事件封装为函数,无需返回值,我们也获取不了返回值

    【2】 通过模块的Process类创建进程对象,关联函数

    【3】 通过进程对象调用start方法启动进程

  • 主要类和函数使用

    Process()
    功能 : 创建进程对象
    参数 : target 绑定要执行的目标函数 
           args 元组,用于给target函数位置传参
           kwargs 字典,给target函数键值传参
           daemon  bool值,让子进程随父进程退出---父进程蹦了,子进程没必要处理了
           
    p.start()
    功能 : 启动进程
    注意 : 启动进程此时target绑定函数开始执行,该函数作为新进程执行内容,此时进程真正被创建
    
    p.join([timeout])
    功能:阻塞等待子进程退出
    参数:最长等待时间,不写就必须等到子进程退出父进程才执行。
​
​

 

 

新进程会原样拷贝原来进程空间,但不会子子孙孙无穷尽也,linux会自动判断到子进程结束。子进程创建那一刻空间相互独立,互不干扰。

​
"""
    创建进程演示
"""
import multiprocessing as mp
from time import sleep
import os
​
a = 1
print("子进程不执行print")#不同于变量a会存到内存,子进程不会套娃循环执行一次。
​
​
# 创建进程执行函数
def fun():
    print("创建子进程开始执行了")
    global a
    print("a = ", a)
    a = 1000
    print(os.getpid())
    sleep(4)
    print("子进程执行结束了")
​
​
# 实例化进程对象绑定函数
process = mp.Process(target=fun)
​
# 启动进程:进程产生,运行绑定函数
process.start()
print("原有父进程干个事情")
# 阻塞等待子进程执行完成
process.join()  # 如果不等子进程执行结束,那么父子进程同时执行。
​
sleep(3)
print("a = ", a)
print("原有父进程干完了", os.getpid())
​
# 由于创建进程也需要事件,父进程可能会优先抢占到时间片先执行了
# 偶尔也可能子进程优先抢占,不存在尊老爱幼。
​

创建的子进程会原样拷贝父进程的内存空间,win与Mac会无线套娃,linux会挑选的自动执行函数部分的进程。这时候父进程依旧往下执行。子进程也往下执行。真实的也是两个进程同时执行,占用同一个打印界面。

  • 进程执行现象理解 (难点)

    • 新的进程是原有进程的子进程,子进程复制父进程全部内存空间代码段,一个进程可以创建多个子进程。

    • 子进程只执行指定的函数,其余内容均是父进程执行内容,但是子进程也拥有其他父进程资源。

    • 各个进程在执行上互不影响,也没有先后顺序关系。

    • 子进程创建后,各个进程空间独立,相互没有影响。

    • multiprocessing 创建的子进程中无法使用标准输入(即无法使用input,因为父进程已经有了)。

 

 

a = 1 子进程不执行,与print一样不执行,不一样的是 a 占用内存空间到进程去,print不占内存空间到子进程中去。在子进程修改全局变量,父进程不受到影响,因为在创建子进程时候已经与父进程的内存空间独立出去了。

"""
    包含参数的进程函数
"""
from multiprocessing import Process
from time import sleep
​
​
# 创建带有参数的进程函数
def worker(sec, name):
    for i in range(3):
        sleep(sec)
        print("I am %s" % name)
        print("I am working...")
​
​
# 位置传参,写成元组,必须有序
# process = Process(target=worker,
#                   args=(2, "Tom"))# 形参只有一个也要写成元组形式
​
# 关键字传参,写成字典,可以无序
# process = Process(target=worker,
#                   kwargs={"name": "Tom", "sec": 2})
​
# 排在前面参数用位置有序传参,排后面用关键字传参数可无序
process = Process(target=worker,
                  args=(2,),  # 必须写成元组,即使有一个元素
                  kwargs={"name": "Tom"},
                  daemon=True  # 子进程随父进程结束而结束,见打印结果只循环两次子进程就结束
                  )
​
process.start()
sleep(5.99)
​
"""
    练习01: 文件拆分
假设有一个大文件 ,将这个文件拆分为2个小文件
按照字节平均分即可.要去两个小文件的拆分过程
同时执行,以提高效率.
"""
# 如果两个进程共用一个打开文件,操作系统就会生成一个偏移量
# 导致读写混乱发生,如果分别打开就不会导致混乱
​
from multiprocessing import Process
import os
​
size = os.path.getsize("bigfile.txt")
​
​
# 复制上半部分
def readfile1(size):
    smallfile1 = open("smallfile1.txt", "wb")
    file = open("bigfile.txt", "rb")
    while True:
        data = file.read(1)
        if file.tell() > (size // 2):
            break
        smallfile1.write(data)
    smallfile1.close()
    file.close()
​
​
​
# 复制下半部分
def readfile2(size):
    smallfile2 = open("smallfile2.txt", "wb")
    file = open("bigfile.txt", "rb")
    file.seek(size // 2)
    while True:
        data = file.read(1)
        if not data:
            break
        smallfile2.write(data)
    smallfile2.close()
    file.close()
​
​
​
process1 = Process(target=readfile1, args=(size,))
process2 = Process(target=readfile2, args=(size,))
​
process1.start()
process2.start()
process1.join()
process2.join()
print("文件拆分结束")
​
​
​

 

 

打开一个文件句柄给两个进程同时操作,共用一个文件偏移量,导致偏移量混乱。

2.2.3 进程处理细节

  • 进程相关函数

    os.getpid()
    功能: 获取一个进程的PID值
    返回值: 返回当前进程的PID 
    os.getppid()
    功能: 获取父进程的PID号
    返回值: 返回父进程PID
    sys.exit(info)
    功能:退出进程
    参数:字符串 表示退出时打印内容,进程退出提示

"""
    创建多个子进程,做多件事情
    三个子进程执行同一个函数意义:通过传参让每个子进程达到不同效果
"""
from multiprocessing import Process
from time import sleep
import os, sys
​
​
def th1():
    sleep(1)
    print("吃饭")
    print(os.getppid(), "--", os.getpid())
​
​
def th2():
    sleep(2)
    sys.exit("不睡觉了")
    print("睡觉")
    print(os.getppid(), "--", os.getpid())
​
​
def th3():
    sleep(4)
    print("打豆豆")
    print(os.getppid(), "--", os.getpid())
​
​
# 循环创建进程:上面的三个进程有相同的ppid,不同的pid
# 所有进程执行完成就永久消失,下次再执行就是新id
jobs = []
for th in [th1, th2, th3]:
    process = Process(target=th)
    jobs.append(process)  # 保存进程对象
    process.start()
​
#等待所有子进程结束,父进程才执行print
for i in jobs:
    i.join()
print("所有事件执行完毕")
​

"""
    升级:如果子进程功能类似,可使用一个函数完成.
         而不是三个函数交给三个子进程完成
"""
from multiprocessing import Process
from time import sleep
import os
​
​
def th(sec, thing):
    sleep(sec)
    print(thing)
    print(os.getppid(), "--", os.getpid())
​
​
jobs = []
for i in [(3, "吃饭"), (2, "睡觉"), (4, "打豆豆")]:
    process = Process(target=th, args=i)
    jobs.append(process)
    process.start()
​
​
for i in jobs:
    i.join()
print("所有事件执行完毕")
​

  • 孤儿进程和僵尸进程

    • 孤儿进程: 父进程先于子进程退出时,子进程会成为孤儿进程,孤儿进程会被系统自动收养,成为孤儿进程新的父进程,并在孤儿进程退出时释放其资源。

    • 僵尸进程: 子进程先于父进程退出,并且父进程又没有处理子进程的退出行为状态,此时子进程就会成为僵尸进程。

      特点: 僵尸进程虽然结束,但是会存留部分进程资源在内存中,大量的僵尸进程会浪费系统资源。Python模块当中自动建立了僵尸处理机制,每次创建新进程都进行检查,将之前产生的僵尸处理掉,而且父进程退出时候,僵尸也会被系统自动处理。

    """
        僵尸进程
    """
    from multiprocessing import Process
    import os
    from time import sleep
    ​
    ​
    def fun():
        print("我(子进程)的id:",os.getpid(), "我要变成僵尸进程")
        print("啦啦啦.....")
    ​
    ​
    for i in range(5):#建立5个僵尸进程
        p = Process(target=fun)#争夺时间片创建id,因此id不是顺序的
        p.start()  #优化2: 自动检测之前的进程是否有僵尸,sleep后只有一个僵尸了,创建太快就来不及检查
        sleep(2)
    ​
    # p.join()  #优化1: 如果子进程是僵尸就回收僵尸进程
    ​
    while True:  # 让父进程不结束:父进程结束那么操作系统自动处理子进程僵尸
        pass  # 查看id状态,发现子进程id变成Z状态即僵尸状态
    #终端只有一个僵尸,因为start清除之前的了。

2.2.4 创建进程类

进程的基本创建方法将子进程执行的内容封装为函数。如果我们更热衷于面向对象的编程思想,也可以使用类来封装进程内容。

  • 创建步骤

    【1】 继承Process类

    【2】 重写__init__方法添加自己的属性,使用super()加载父类属性

    【3】 重写run()方法

  • 使用方法

    【1】 实例化对象

    【2】 调用start自动执行run方法

"""
    自定义进程类:面向对象的编码风格,本质与前面进程一样
"""
from multiprocessing import Process
from time import sleep
​
​
# 创建进程和进程执行内容(函数)都写在类中 --> 面向对象
class MyProcess(Process):
    def __init__(self, value):
        self.value = value
        super().__init__()
​
    # 你想让进程做什么就写什么函数
    def func(self):
        for i in range(self.value):
            sleep(2)
            print('子进程执行了一些内容')
​
    # 重要:运行start自动执行run方法,作为进程内容
    def run(self):
        self.func()
​
​
if __name__ == '__main__':
    p = MyProcess(3)
    p.start()  # 创建进程
​
#伪代码:为什么调用start自动执行run
# class Process:
#     def __init__(self,target=None):
#         self._target = target
#
#     def run(self):#run是接口函数,帮你规定了,里面就是pass,专门给你从写用
#         self._target()
#
#     def start(self):
#         # 创建进程
#         self.run()
​
​
​
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dpq666dpq666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值