并发编程

概要
 
  1. 操作系统发展史
  2. 多道技术
    • 实现该技术需包含
    • CPU工作机制
    • 并发现象
  3. 进程理论
    • 进程与程序的区别
    • 单核CPU中,起多个进程怎么办,怎么调度?
    • 进程的三状态
    • 同步与异步,  阻塞与非阻塞
  4. 进程其他
    • 怎么用代码创建进程
    • join方法
      • 语法格式
      • 作用
    • 进程间通信特点: 之间隔离,无法通信
    • 进程对象方法
 
详细
 
 
1 操作系统发展史
 
"""学习并发编程其实就是在学习操作系统的发展史(底层逻辑)"""
1.穿孔卡片时代
    穿孔卡片即程序
    CPU的利用率极低
    其中: 制作程序即卡片,输入程序即将卡片输入机器期间CPU都未工作,所以效率低
2.联机批处理系统
    将多个程序员的程序一次性录入磁带中 之后交由输入机输入并由CPU执行
3.脱机批处理系统
    现代计算机的雏形(远程输入 高速磁带 主机)
    其中: 远程输入到的设备相当于硬盘; 高速磁带相当于内存; 主机就是指CPU;
    根据木桶效应: CPU处理速度特别快,而硬盘读取速度太慢,所以才有了高速磁带

 

2 多道技术
 
2.1 实现该技术需包含:
 
  • 服务员: 单核CPU(看清楚,不是多核CPU)
  • 服务员的能力:
    • 能快速切换到其他程序提供服务
    • 离开某服务对象前会记住当时的服务状态,下次再服务时无缝衔接
 
2.2 补充- CPU工作机制
 
  • 理解
    • 做个比喻: CPU是计算机这个公司的核心员工;操作系统是公司CEO
  • CPU工作机制
    • 当某个程序进入IO状态的时候 操作系统会停止CPU对其服务
    • 当某个程序长时间占用CPU的时候 操作系统也会停止CPU对其服务
 
 
2.3 并发现象
 
2.31定义
  • 看起来某种事情同时发生的现象
    • 看起来同时发生,实际并不是
    • 相似词语
      • 并行:多个程序同时执行(每个程序都有为其提供服务的服务员)
 
2.32 实际案例比喻
  • 比如: 一个饭店只有一个服务员,但是却能同时服务五桌客人,这种"同时服务五桌客人"的现象就叫并发
  • 实现办法:
    • 利用某桌客人看菜单,喝茶时间服务其他桌客人.这样每桌客人都感觉被服务;
 
  • 需要服务员的能力
    • 1) 跑的快,以至于每桌客人在需要服务的时候服务员都在身边
    • 2) 能记住每桌客人之前的服务状态,以便下次无缝衔接,这样每桌客人才不会被感觉敷衍
 
 
 
3 进程理论
 
3.1 进程与程序的区别
 
程序: 存储在硬盘上的有一定功能的代码(死的)
进程: 正在运行的程序(活的)
 
 
3.2 单核CPU中,起多个进程怎么办,怎么调度的?
 
最优方法
  • 多级队列 + 时间片轮转法
    • 多级队列
      • 把程序执行次序分层,先运行第一层的程序,再运行第二层的程序,以此循环到最后一层,然后再到第一层,再循环
    • 时间片轮转法
      • 将1sec分成几份,越是下一层执行的程序被分配的份额越多
 
  其他方法
  • 先来先服务
    • 若前边有一个花费时间特久才运行完程序,后边有一个很短时间就可以运行完的程序,对后边的这种短时间就可以运行完的程序不友好
  • 短作业优先调度算法
    • 若有几千个短时间就能运行完的程序,先运行完他们再运行耗费时间长的程序,对后边这个需要长时间运行的程序不友好
 
 
3.3 进程的三状态(程序运行的三种状态***)
 
 
 
1) 三状态
  • 就绪态
  • 运行态
  • 阻塞态
 
2) 三状态转换图
  • 新进程
    • 就绪态 -> 运行态
  • 已经在运行的进程
    • 运行态 ->阻塞态 ->就绪态 ->运行态
    • 运行态 (时间片到)-> 就绪态 -> 运行态
 
3) 理解
 
阻塞态: 操作系统恨不得CPU每秒工作,所以发生那么凡是需要让cpu等待的操作都是阻碍其工作的,如 用户输入;
 
 
3.4 同步与异步,  阻塞与非阻塞
 
  • " 同步与异步"和"阻塞与非阻塞"组合使用
    • 最快组合: 异步非阻塞
 
3.41 同步与异步(******)
 
'''用于描述任务的提交方式'''
 
同步
提交一个请求后,期间不做任何事,原地等待结果返回
 
异步
提交一个请求后,不原地等待任务的返回结果, 直接去做其他事 结果由反馈机制自动提醒
 
 
3.42 阻塞与非阻塞(******)
 
'''用于描述任务的执行状态'''
 
阻塞:阻塞态
非阻塞:就绪态 运行态
 
 
4 进程其他
 
4.1 创建进程-代码方式
  • 创建进程: 是在内存中申请一块空间,然后在里边运行程序
 
两种方式
 
方式1
 
xxx.start()
  • # 执行此代码即告诉操作系统开设一个新的进程 异步提交(原来的主进程,本页面程序仍在运行)
  • # 操作系统听到x.start(), 就准备开设一个子进程,需要准备以下才开设,不是马上
from multiprocessing import Process
import time
import os


def test(name):
    print(os.getpid())  # 获取进程号
    print(os.getppid())  # 获取父进程号
    print('%s正在运行' % name)
    time.sleep(3)
    print('%s已经结束' % name)




if __name__ == '__main__':
    p = Process(target=test, args=('jason',))  #生成一个进程对象;即把test函数申请一块内存空间,执行
    p.start()  # 告诉操作系统开设一个新的进程,代码能执行到这, 表明有本页面是一个代码正在内存的一块空间运行,即有一个进程在进程; 操作系统马上在内存空间弄一块空间给test程序,原进程还在运行,所以是异步提交
    print(os.getpid())  # 
    print('主进程')  # 右键"运行"本页代码,就是开了一个进程

"""
在windows中开设进程类似于导入模块
    从上往下再次执行代码
一定需要在__main__判断语句内执行开设进程的代码


在linux中是直接将代码完整的复制一份执行
    不需要在__main__判断语句内执行
"""

 

方式2  面向对象方式

from multiprocessing import Process
import time

class MyProcess(Process):  # 一定要继承Process,因为
    def __init__(self, name):
        super().__init__()  # 执行super()类名对应的init方法
        self.name = name  # 给类对象加一个name属性


    def run(self):
        print('%s正在运行' % self.name)
        time.sleep(3)
        print('%s已经结束' % self.name)


if __name__ == '__main__':
    p = MyProcess('jason')
    p.start()
    print('主进程')

 

4.2 xxx.join 方法
  • 功能: 主进程等待子进程结束后运行
 
代码案例
 
多个子进程串行运行
from multiprocessing import Process
import time

def test(name, n):
    print('%s is running' % name)
    time.sleep(n)
    print('%s is over' % name)

if __name__ == '__main__':
    p_list = []
    start_time = time.time()
    for i in range(1, 4):
        p = Process(target=test, args=(i, i))
        p.start()
        p_list.append(p)
        p.join()  #第一个子进程运行完,运行主进程,也就是进入下一for循环,开启第二个子进行,运行完,运行主进程代码,也就是进入下一for循环,开启第三个子循环,运行结束,然后再运行主程序,即接下来代码

    print(time.time() - start_time)

    print('主进程')
 
多个子进程异步(并行)运行
from multiprocessing import Process
import time




def test(name, n):
    print('%s is running' % name)
    time.sleep(n)
    print('%s is over' % name)




if __name__ == '__main__':
    p_list = []
    start_time = time.time()
    for i in range(1, 4):
        p = Process(target=test, args=(i, i))
        p.start()
        p_list.append(p)

    for p in p_list:
        p.join()
    print(time.time() - start_time)


    print('主进程')

 

4.3 进程之间数据相互隔离,无法通信
  • "内存中两块空间"即两个进程之间数据是相互隔离的
 
代码验证
from multiprocessing import Process


money = 100




def test():
    global money
    money = 999
    print(money)




if __name__ == '__main__':
    p = Process(target=test)
    p.start()
    # 先确保子进程运行完毕了 再打印
    p.join()
    print(money)  # 输出结果先999;下一行100


# 结果(想下为啥)
# 999
# 100

 

4.4 进程对象方法
 
4.41 查看进程号
 
1)查看当前进程号
方式1
方式2
 
2)查看当前进程号的父进程号
 
查看进程号 代码
import os
from multiprocessing import Process,current_process
import time


print(os.getpid(), os.getppid())  # 获取当前进程号-方式1  获取当前进程的父进程号
# print(current_process().pid)  # 获取当前进程号-方式2


time.sleep(40)

 

演示及cmd中查看进程号对应进程
 
 
4.42 其他-  1)查看进程名字, 2)杀死子进程, 3)判断当前进程是否存活
 
import os
from multiprocessing import Process, current_process
import time




def test(name, n):
    print('%s is running' % name)
    time.sleep(n)
    print('%s is over' % name)




if __name__ == '__main__':
    p = Process(target=test, args=('jason', 3))
    p.start()
    print(p.name)   # 查看进程名字
    p.terminate()  # 杀死子进程
    time.sleep(0.1)  # 主进程睡0.1才可看出"下行代码"效果;不加本行,因为操作系统反应需要时间,下行结果是True
    print(p.is_alive())  # 判断当前进程是否存活
    print('')

 

posted @ 2022-01-13 23:38  tslam  阅读(44)  评论(0编辑  收藏  举报