Python进程及multiprocess

前言

这几年一直在it行业里摸爬滚打,一路走来,不少总结了一些python行业里的高频面试,看到大部分初入行的新鲜血液,还在为各样的面试题答案或收录有各种困难问题

于是乎,我自己开发了一款面试宝典,希望能帮到大家,也希望有更多的Python新人真正加入从事到这个行业里,让python火不只是停留在广告上。

微信小程序搜索:Python面试宝典

或可关注原创个人博客:https://lienze.tech

也可关注微信公众号,不定时发送各类有趣猎奇的技术文章:Python编程学习

进程

程序:程序是一个存储在磁盘上某个目录中的可执行文件

进程:程序在内存中执行的实例被称为进程

进程属性

  • 独立的地址,独立的内存空间
  • 唯一的身份标识符,PID(该值可复用,延迟复用算法),(CPU处理进程任务,会使用时间片轮转调度算法,一个CPU核心不会在一个进程停留过多时间)
  • 进程启动者身份信息(UID),同样可以维护当前进程的权限等

PID为0:该进程为调度进程(idle进程),是系统初始化的第一个进程,是其他进程的祖先

PID为1:该进程是init进程,启动unix系统,由进程0创建,完成剩余的系统引导工作

PID为2:该进程为内核线程管理进程

在linux操作系统下,每一个进程都有自己的pcb,也叫做进程控制块,是系统为了管理进程设置的一个专门的数据结构,用来记录和管理进程的属性及特征、维护进程的状态和变化过程

PCB(进程控制块)是系统感知进程存在的唯一标识,通常处于系统内存中的一块连续地址,对进程的管理都需 要PCB中维护的内容

  • PCB中维护的内容
  1. 进程标识符、内部|外部
  2. 调度信息:进程状态、进程优先级等其他
  3. 进程状态
  4. 使用的IO设备、屏幕、文件、网卡等等
  5. 处理器信息:寄存器、计数器、堆栈指针

进程状态

  • RTASK_RUNNING

就绪/运行状态

实际在CPU上所运行进程为运行状态,一些可执行的但尚未被调用的处于就绪状态
该状态进程会被存放到CPU的可执行队列中,去由CPU具体分配资源,进行工作

  • STASK_INTERRUPTIBLE

可中断睡眠状态

这样的进程可能处于一种等待状态、等待socket链接、IO、信号量、CPU等等,而被挂起
等待的进程,会存储在一个专门的等待队列中、如果等待事件完成、等待队列中的进程将被唤醒

  • DTASK_UNINTERRUPTIBLE

不可中断睡眠状态

一般处于这样状态的是一些内核进程、我们是无法直接使用kill -9的方式中断杀死
这样的进程、也是处于一种睡眠状态,暂时挂起
比如当我们需要使用内核函数对某些设备进行读写操作、为了避免在这个过程中,使用中断或者异步信号的方式使进程终止,而导致进程与设备交互过程被打断(陷入不可控状态);就需要该状态对这样的进程进行保护

  • TTASK_STOPPED|TASK_TRACED

暂停状态

使进程停止,可以向进程发送SIGSTOP信号
使进程开启,发送SIGCONT信号,将进程回复为TASK_RUNNING状态
TASK_TRACED一般为断点,更为安全,但需要使用ptrace系统函数来开启进程或调试进程结束

  • ZTASK_DEAD-EXIT_ZOMBIE

退出或僵尸状态

进程在退出的过程中,处于TASK_DEAD状态
每个进程在退出时,所占用的资源都将被回收、但是会保留一个进程相关信息的数据块
当这个数据库里所维护的东西都被释放掉了,所剩余的就是被作僵尸(会一直占用进程ID)
保留的数据块中药维护进程的退出码、PID等等信息

  • XTASK_DEAD-EXIT_DEAD

停止状态

该过程非常短暂,代表进程即将被销毁
释放资源

CPU中断

中断是由硬件或软件发送的一种信号,也叫IRQ(中断请求)

CPU处理事情主要两种方式:

  1. 中断
  2. 轮询

所有的linux操作系统都是基于中断请求来让CPU进行工作;一个CPU一旦接收到了一个中断请求,就会暂停执行手头的工作转而去处理这个中断的请求,并且调用一个叫做中断处理器的特定程序,当处理完这个中断请求之后,他会恢复执行之前被中断的工作

僵尸、孤儿

正常情况下,子进程运行完毕,父进程是需要回收子进程种的内存资源;linux系统会在进程结束后,释放进程所占用的资;比如,内存,打开的文件,一些句柄等等。同时会在一个额外的数据结构体中延续保存:PID,退出状态,运行时间等信息。

但如果子进程已经结束了任务,父进程迟迟没有对他进行资源收集和释放,那么就会造成很多这样的结构体留在内存中,进程的ID值也是有限的,僵尸进程过多,也无法生产新的进程,导致系统性能大幅度下降

  • 僵尸进程:父进程活着,子进程已经完成了任务,但是父进程不做回收工作

如果父进程没有回收子进程的资源,直接就退出了,那么此时,执行完毕的子进程,由于没有人回收资源,会形成孤儿进程

但是孤儿进程并不可怕,在linux系统中,还会有init进程前来帮助回收这些孤儿进程,做善后工程

  • 孤儿进程:父进程退出,子进程执行完毕,等待回收,一般会被init进程接管回收

同步异步

**同步:**多个线程或进程在发出或者接收到请求(或反馈)的时候,只有等到了结果或反馈,才能接着干下面的事情

异步: 多个线程或进程在发出或者接收到请求(或反馈)的时候,不需要等到结果,也可以继续干一些别的事情

mutliprocessing

from multiprocessing import Process

该模块自Python 2.6版本起引入,允许为多核或多CPU派生进程,并且提供了方便我们实现进程同步及进程通信等API;由于子进程被创建出后,防止僵尸进程的产生,一定要注意使用join函数在父进程中做子进程退出后的资源收集

  • 创建一个进程
p1 = Process(target=None, name=None, args=(), kwargs={},)
'''
target: 子进程调用的对象
agrs: 调用对象的元组参数
kwargs: 调用对象的字典参数
name: 别名
'''
  • 支持的属性方法
p.run = func
# 如创建了一个关于Process的进程实例,但是并没有给定执行函数参数,可以通过run类来在运行中赋值创建
p.start()
# 开启进程
p.is_alive()
# 返回当前存活进程
p.join(timeout=None)
# 阻塞等待进程结束,并回收;join函数部分的timeou参数用来指明等待回收的时间,时间一到,就结束阻塞	行为,不在继续等待子进程结束;子线程依然可以继续运行,所以非阻塞可以是在一个循环中来进行
p.terminate()
# 直接杀死进程,该进程不会在执行之后的工作,在该函数执行完成之后,也要使用join用来更新状态及回收
p1.daemon
# 设置该值为True时,父进程结束,该进程结束,且自己不能产生新进程,在p.start()之前设置
p1.exitcode
# 进程在运行时该状态码为None,其他表示结束
p1.name
# 进程的名字,在Process类初始化的时候可以赋值传参定义
p1.pid
# 当前进程PID
  • multiprocessing创建多进程
from multiprocessing import Process,current_process
import time

def work_1():
    print('this is work_1:',current_process().name)
    time.sleep(1)
    print("work_1 is over.")
def work_2():
    print('this is work_2:',current_process().name)
    time.sleep(1)
    print("work_2 is over.")
def work_3():
    print('this is work_3:',current_process().name)
    time.sleep(1)
    print("work_3 is over.")

def main():
    p1 = Process(target=work_1)
    p2 = Process(target=work_2)
    p3 = Process(target=work_3)
    process_list = [p1,p2,p3]

    t_start = time.time()
    for p in process_list:
      	p.start()
    while True:
        for p in process_list:
          	p.join(timeout=0)
        if not p1.is_alive() and not p2.is_alive() and not p3.is_alive():
        	break
    t_end = time.time()

    print('%.2f' % (t_end - t_start))
if __name__ == '__main__':
	main()
# 当前程序的结果为1s左右,并非3s,说明是一个并发的多进程行为
  • 继承方式创建进程
class Work(Process):
	def __init__(self):
		super().__init__()
    def run(self): #重写run函数
      	pass
p = Work()
p.start()
p.join()

在继承Process类创建好一个实例后,可以直接通过start函数开启进程

start会查找子类及父类中可用的run函数来实现业务执行

  • 继承创建进程的代码示例
from multiprocessing import Process,current_process

class Work(Process):
    def __init__(self):
        super(Work,self).__init__() # super函数 调用父类的一个方法
        self.num = 1
    def run(self):
        self.num += 1 
        print(self.num)

def main():
    p1 = Work()
    p1.start()
    p1.join()

if __name__ == '__main__':
	main()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

李恩泽的技术博客

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

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

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

打赏作者

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

抵扣说明:

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

余额充值