并发编程
并发与串行
程序默认执行方式是串行,即自上而下,当前任务执行完毕才能执行下一个任务,比较浪费时间
问题举例:
从硬盘读取大文件
执行input
学习并发的目的
编写可执行多个任务的程序,提高效率
串行和并发都是程序处理任务的方式
如何实现并发
多进程 (核心原理:多到技术)
多线程
协程
进程和程序
程序是 一堆代码放在一个文件中 通常后缀为exe 原本是存储在硬盘上的
进程是 将代码从硬盘读取到内存然后执行 产生的
进程是由程序产生的
一个程序可以产生多个进程,例如qq多开 每一个进程都具备一个PID 进程变编号 且是唯一的
多道技术
空间复用
时间复用 (切换+保存)
切换的时机:遇到IO或执行时间超出阈值
一个进程的三种操作
阻塞
非阻塞
运行
并发重要概念
串行:自上而下顺序执行
并发:同时执行多个任务,但本质是在不同进程间切换,由于速度非常快,所以感觉是同时运行
并行:是真正的同时运行,但需要具备多核CPU,几核CPU就能并行几个任务,当任务数量超过核心数还是并发执行
三个概念都是用于描述处理任务的方式
阻塞:指程序遇到了IO操作,无法执行代码的 一种状态
input默认是一个阻塞操作
非阻塞:指的是程序没有遇到IO操作的一种状态
我们可以用一些手段非阻塞的操作变成非阻塞的操作,例如非阻塞的socket
PID和PPID
PID 是当前进程的编号
PPID 是父进程的编号
注意:当我们运行py文件时其实运行的是python解释器
访问PID与PPID
import os
os.getpid()
os.getppid()
join的使用
from multiprocessing import Process
import time
import random
def task(n):
time.sleep(random.randint(1, 3))
print('-------->%s' % n)
if __name__ == '__main__':
ps = []
for i in range(1, 4):
p = Process(target=task, args=(i,))
p.start()
ps.append(p)
for i in ps:
i.join()
print('-------->4')
from multiprocessing import Process
import time
import random
def task(n):
time.sleep(random.randint(1,3))
print('-------->%s' %n)
if __name__ == '__main__':
p1=Process(target=task,args=(1,))
p2=Process(target=task,args=(2,))
p3=Process(target=task,args=(3,))
p1.start()
p1.join()
p2.start()
p2.join()
p3.start()
p3.join()
print('-------->4')
进程对象常用属性
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中 已经封装了处理僵尸进程的操作 ,无需关心