Day 31 并发编程

并发编程

并发和串行

  • 程序默认的执行方式是串行,即程序自上而下一行一行执行,必须把当前任务执行完毕才能执行下一个任务

学习并发的目的

就是编写可以同时执行多个任务的程序,从而提高效率

串行和并发都是程序处理任务的方式

## 实现并发的方式

  1. 多进程
  2. 多线程
  3. 协程

进程是什么

进程是指正在运行的程序,是操作系统调度以及进行资源分配的基本单位

当程序从硬盘读取到内存中运行的时候进程就产生了

多进程:指的是同一时间有多个程序被装入内存并执行

多进程的实现原理其实就是操作系统调度进程的原理

操作系统是什么

操作系统就是一款特殊的软件

操作系统和普通软件的区别:

  1. 操作系统可以直接与硬件交互
  2. 操作系统是受保护的,不能直接被修改
  3. 操作系统更加长寿,一旦完成基本不会修改,如系统内核

操作系统的主要功能

  1. 隐藏了硬件复杂的操作,提供了简单的api接口
  2. 将硬件的资源竞争变得有序可控

GUI图形化用户界面

操作系统发展史

掌握多道技术的实现原理,就是多进程的实现原理

第二代计算使用的是批处理系统,存在以下三个问题

  1. 需要人为参与
  2. 任务串行执行
  3. 程序员调试效率低

第三代计算机

  1. 使用SPOOLING联机计数

  2. 多道技术
  3. 多终端多用户

第四代计算机

大规模集成电路+多用户多终端

特点:具备GUI图形化界面,普通人也可以使用

多道技术

实现原理:

  1. 空间复用

    ​ 同一时间加载多个任务到内存中,多个进程之间的内存区域需要相互隔离,这种隔离是物理隔离,为了保证数据安全性

  2. 时间复用

    ​ 操作系统会在多个进程之间做切换

    ​ 切换进程的两种情况:

    ​ 1.但给一个进程遇到IO操作时会自动切换

    ​ 2.当任务执行时间过长(超时)强制切换

    ​ 切换进程前需要记录当前进程的状态,同时频繁的切换也消耗系统资源

    ​ 当所有任务没有IO操作时,切换执行反而降低了效率,但是为了保证并发执行必须要牺牲效率

多道技术总结:切换+保存

有了多道技术,计算机就可以同时并发处理多个任务

并发编程中的重要概念

串行:从上到下依次执行

并发:多个任务同时执行,本质是多个进行不断在切换,由于切换速度快,所以感觉是同时运行

并行:真正意义上的同时运行,有几个核心就能并行几个任务,如果超过核心数就使用并发执行

以上三个都是用于描述处理任务的方式

一个进程的三种状态

阻塞:当程序遇到IO操作,无法继续执行代码时的一种状态

非阻塞 :程序没有遇到IO操作的状态,正常运行

就绪:程序等待CPU运行

阻塞非阻塞也可以用来描述执行任务的方式

三种状态的互相切换

程序正常运行,当运行时间超过阈值,会进入就绪状态,等待cpu执行完其他的进程后,恢复到运行状态继续执行

当程序遇到IO操作,会进入阻塞状态,当结束阻塞状态后,程序会进入就绪状态,等待cpu空闲后继续执行本程序

野指针和僵尸指针

野指针:内存中的地址被删除

僵尸指针:内存中的地址因为某种原因没有被清除

进程的创建和销毁

创建:

  1. 用户交互式请求
  2. 有一个正在运行的程序调用了开启进程的接口
  3. 一个批处理作业开始
  4. 系统初始化

销毁:

  1. 任务完成
  2. 强制结束 windows:taskkill/linux:kill
  3. 程序异常

进程和程序

程序是一堆代码存放在文件中

进程就是程序中的代码从硬盘读入内存后产生的

进程是由程序产生的

一个程序可以产生多个进程,每一个进程都有一个唯一的PID

进程的层次结构

在linux中进程具备父子关系,是一个树状结构,可以相互查找到对方

在windows中,没有层级关系,父进程可转让子进程的句柄

父进程与子进程:例如qq打开了浏览器,qq就是父进程,浏览器就是子进程

PID和PPID

PID是当前进程的编号

PPID是当前程序的父进程的编号

我们运行py文件的时候其实是运行python解释器,所以运行py文件的父进程就是python解释器

import os
os.getpid()
os.getppid()

python如何使用多进程

1. 导入multiprocessing中的Process类 实例化这个类 指定要执行的任务target

import os
from multiprocessing import Process

def task():
    print("this is sub process")
    print(f"sub process id {os.getpid()}" )

if __name__ == '__main__':
    p = Process(target=task)
    p.start() 
    print("this is parent process")
    print(f"parent process is: {os.getpid()}")
    print("over")

linux和windows开启进程的方式区别

linux会将父进程的内存数完整复制一份给子进程

windows会导入父进程的代码,从新执行一遍,来获取需要处理的任务,所以在编写代码时如果是windows,需要把开启进程的代码放在main判断中

2. 导入multiprocessing中的Process类 继承这个类 覆盖run方法 将要执行的任务放入run中开启进程是会自动执行该函数

from multiprocessing import Process
import os


class Downloader(Process):

    def __init__(self,url,size,name):
         super().__init__()
         self.url = url
         self.size = size
         self.name = name

    def run(self):
        print(os.getpid())
        pass

if __name__ == '__main__':
    m = Downloader()
    m.start()
    print("parent over",os.getpid())

进程之间内存相互隔离

from multiprocessing import  Process
import os,time

a = 257

def task():
    global a
    print("2",a,id(a))
    a = 200
     

if __name__ == '__main__':
    p = Process(target=task)
    p.start() 

    time.sleep(4)
    print(a)

join函数

就是让主进程等待子进程运行完再执行

from multiprocessing import Process
import time

def task1(name):
    for i in range(10000):
        print(f'{name} run')

def task2(name):
    for i in range(100):
        print(f'{name} run')

if __name__ == '__main__': 
    p1 = Process(target=task1,args=("p1",))# args 是给子进程传递的参数 必须是元组
    p1.start()   

    p2 = Process(target=task2,args=("p2",))
    p2.start()  

    p2.join() 
    p1.join()

    print("over")

进程对象的常用属性

if __name__ == '__main__':
    p = Process(target=task,name="老司机进程")
    p.start()
    # p.join()
    # print(p.name)
    # p.daemon #守护进程
    # p.join()
    # print(p.exitcode) # 获取进程的退出码   就是exit()函数中传入的值
    # print(p.is_alive())  # 查看进程是否存活
    # print("zi",p.pid) # 获取进程id
    # print(os.getpid())
    # p.terminate()  #终止进程  与strat 相同的是 不会立即终止,因为操作系统有很多事情要做  
    # print(p.is_alive())

僵尸进程和孤儿进程

孤儿进程:当父进程已经结束,而子进程还在运行,子进程就成为孤儿进程,孤儿进程有其存在的意义,没有不良影响,孤儿进程会被操作系统接管

僵尸进程:当一个进程已经结束了,但是他仍然还有一些数据存在,此时称之为僵尸进程

在linux中,有这么一个机制,父进程无论什么时候都可以获取到子进程的一些数据;子进程任务执行完毕后,确实结束了但是仍然保留了一些数据,目的是为了让父进程能够获取这些信息;linux中,可以调用waitpid来彻底清除子进程的残留信息

python中已经封装了处理僵尸进程的操作

猜你喜欢

转载自www.cnblogs.com/masterjian924/p/11124038.html
今日推荐