并发01--进程01:操作系统发展过程、进程介绍

1.操作系统的发展史

# 其实主要就是围绕CPU的利用率问题
1.穿孔卡片 2.联机批处理系统 3. 脱机批处理系统

2.多道技术

# 单核实现并发的效果

并发:看起来像同时运行的就可以叫做并发,伪并行
并行: 真正意义的同时运行

ps:明星出轨>>>星轨(指微博能承受的并发量)

空间复用:空间上,多个程序公用一套计算机硬件
时间复用:时间上,切换+保存状态 (多个任务之间快速切换执行,感觉上是同时执行)


# 切换分为两种
1.当一个程序遇到IO操作时,操作系统会立刻剥夺该程序的cpu执行权限
(提高了CPU的利用率,并且不影响该程序的执行效率)

2.当一个程序长时间占用cpu,操作系统也会离开剥夺该程序的cpu执行权限
(提高了cpu的利用率,但降低了该程序的执行效率,实现了并发的效果)

3.进程

"""
程序就是一堆死代码     死的
进程则是正在执行的程序  活动 (就是程序的实例)
"""

# 进程的调度算法
先来先服务调度算法
短作业优先调度算法
时间片乱转法 + 多级反馈队列

3.1 进程运行的三状态图

就绪态:一切程序必须要先过就绪态才能加入运行态
运行态:正在被cpu执行
堵塞态:程序遇到IO操作了

理想:我们希望我开发的程序一直处于就绪态与运行态之间

3.2 两对重要概念

  • 同步与异步
"""任务的提交方式"""
同步:任务提交后,原地等待任务的返回结果期间不做任何事情

异步:任务提交后,不原地等待任务的返回结果,继续执行下一行代码
    结果由异步回调机制做处理
  • 阻塞与非阻塞
"""程序的运行状态"""
阻塞:阻塞态

非阻塞:就绪态、运行态

上面的两队概念通常会组合出现,但最高效常用的就是异步非阻塞。

# 1. 同步与异步针对的是函数/任务的调用方式:
同步就是当一个进程发起一个函数(任务)调用的时候,一直等到函数(任务)完成,而进程继续处于激活状态。
而异步情况下是当一个进程发起一个函数(任务)调用的时候,不会等函数返回,而是继续往下执行当,
函数返回的时候通过状态、通知、事件等方式通知进程任务完成。

# 2. 阻塞与非阻塞针对的是进程或线程:
阻塞是当请求不能满足的时候就将进程挂起,而非阻塞则不会阻塞当前进程

3.3 开启进程的两种方式

3.3.1 利用 multiprocessing 模块下的 Process 类

代码开启进程和线程的方式,代码书写基本是一样的。

# 第一种,函数式(必须掌握)
from multiprocessing import Process
import time


def task(name):
    print('{} is running'.format(name))
    time.sleep(3)
    print('{} is over'.format(name))


再多建/新建一个进程,就相当于你现在执行的程序代码,复制一份,在内存中 新建一块空间来执行。
# 重点:
windows 操作系统下,创建进程一定要在main内创建,
因为windows下创建进程类似导入模块的方式,会从上往下依次执行代码,
若不在main内中创建,就会一直处于创建进程的死循环中。


# 例如: 不在main中创建进程,而在这里创建进程,
在这里的话,代码读取在这个位置后,就会去建新的进程,
然后新的进程又会执行该代码(代码跟这个完全一样,是复制的)
所以新的进程读取到这里,又再会去新建一个进程,产生一个一直在建新的进程的 死循环中。


# 故所有的执行代码,调用函数,或者实例化对象的时候,都将代码放在main中
# main内代码只会在本脚本程序在执行时调用,而在作为模块导入时,不会执行

if __name__ == '__main__':
    # 1.实例化 Process 进程类的对象,
    # 参数 target= 执行函数,注意执行函数不能调用,不能加()
    # 参数 args=(),()内 以元祖的方式 传递 该执行函数的参数
    p = Process(target=task, args=('jason',))
    # 容器类型哪怕只有1个元素,一定也要用逗号隔开。防止出现小bug

    # 2.开启进程
    p.start()  # 告诉操作系统,开始创建一个进程。

    # 模拟执行其他代码
    print('主')

    
# 执行结果:
主                     由 当前代码 执行的结果
jason is running       由 新的进程打印出来的
jason is over

# 根据执行结果:
先打印了print('主'), 再打印了p.start()的返回结果。
说明 p.start() 开启进程这一步,是一个异步操作。
因为按照代码执行顺序下来,p.start() 在 print('主' )之前,
但是并没有等到 p.start() 的返回结果后,CPU就先打印了print('主')。

    
# 第二种,类继承式
from multiprocessing import Process
import time


class Myprocess(Process):
    def __init__(self, name1):
        super().__init__()
        self.name1 = name1

    def run(self):
        print('{} is running'.format(self.name1))
        time.sleep(3)
        print('{} is over'.format(self.name1))


if __name__ == '__main__':
    p = Myprocess('jason')
    p.start()
    print('主')

# 类的继承方式,将进程需要执行的函数,写在 run()中,覆盖父类 Process的run方法,
# 若是有参数,需要定义在实例化 构造函数 __init__(self,arg)  -----> arg:参数变量名

3.3.2 方法介绍

p.start():  # 启动进程,并调用该子进程中的p.run()
p.run():  # 进程启动时运行的方法,正是它去调用target指定的函数
    我们自定义类的类中一定要实现该方法

p.terminate(): # 强制终止进程p,但不会进行任何清理操作
    如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。
    如果p还保存了一个锁,那么也将不会被释放,进而导致死锁
    
p.is_alive(): # 如果p仍然运行,返回True

p.join([timeout]): # 主线程等待p终止
    强调:是主线程处于等的状态,而p是处于运行的状态
    timeout是可选的超时时间
    
    需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
    
# 开启进程:p.start() 或 p.run() 

3.3.3 属性介绍

p.daemon:  # 设置p为守护进程 默认值为False,
    如果设为True,代表p为后台运行的守护进程,
    当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程
    必须在p.start()之前设置

p.name:  # 进程的名称

p.pid:  # 进程的pid

p.exitcode:  # 进程在运行时为None、如果为–N,表示被信号N结束(了解即可)

p.authkey:  # 进程的身份验证键
    默认是由os.urandom()随机生成的32字符的字符串。
    这个键的用途是为涉及网络连接的底层进程间通信提供安全性
    这类连接只有在具有相同的身份验证键时才能成功(了解即可)

3.3.4 总结

# 创建进程就是在内存中申请一块内存空间将需要运行的代码丢进去
一个进程对应在内存中就是一块独立的内存空间
多个进程对应在内存中就是多块独立的内存空间

# 进程与进程之间数据默认情况下是无法直接交互的,如果想交互,可以借助于第三方工具、模块

3.4 join方法

join是让主进程等待子进程代码运行结束之后,再继续运行。不影响其他子进程的执行。

# 一个需求,等到子进程的结果返回后,再继续执行当前主进程的代码。
# 本例就是先打印 子进程 task()的结果,再打印 print('主')
from multiprocessing import Process
import time


# 案例三 (改写案例二)
def task(name, n):
    print('{} is running'.format(name))
    time.sleep(n)
    print('{} is over'.format(name))


if __name__ == '__main__':
    start_time = time.time()
    # 先开启全部的子进程
    p_list = []
    for i in range(1, 4):
        p = Process(target=task, args=('jason', i))
        p.start()
        p_list.append(p)

    # 再将子进程全部join()
    for p in p_list:
        p.join()

    # 当前主进程的执行代码
    print('主', '运行时间:{}'.format(time.time() - start_time))

3.5 进程之间数据相互隔离的(默认情况下)

# 验证进程之间数据相互隔离
from multiprocessing import Process

money = 100


def task():
    global money  # 局部命名空间中,修改全局变量
    money = 666
    print('子进程中money:{}'.format(money))


if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    p.join()
    print('主进程中money:{}'.format(money))
    
# 打印结果:
子进程中money:666
主进程中money:100

# 说明子进程中修改money的函数确实运行了,但是只是作用于子进程中,并没有修改到主进程的money。完成验证

ps:

# 作为一名python程序员当你遇到一个功能的时候,第一时间你可以考虑是否有对应的模块已经帮你实现功能

"""
人工智能相关参考资料:
聊天机器人(自动回复等):http://www.turingapi.com/
文字、人脸、图像识别:https://www.xfyun.cn/?ch=bd05&b_scene_zt=1
智能写诗等:http://ai.baidu.com/creation/main/demo
天行数据:https://www.tianapi.com/
"""
posted @ 2021-05-26 17:12  Edmond辉仔  阅读(19)  评论(0编辑  收藏  举报