并发编程
-
什么是并发编程
并发指的是多个任务同时被执行,并发编程指的是编写支持多任务的应用程序
并发编程中重要的概念*******
1串行:自上而下顺序执行
2并发:多个任务同时执行,但是本质上是在不同进程间切换执行,由于速度快所以感觉是同时进行的
3并行:是真正的同时进行,必须具备的是多核CPU,有几个核心就能并行几个任务,当任务数量超过核心数,任务进行并发
遇到的状态:
阻塞和非阻塞可以用来描述执行任务的方式
1阻塞:程序遇到了IO操作,无法继续执行代码一种
- input()默认是一个阻塞操作
2非阻塞:程序没有遇到IO操作的一种
- 我们可以用一些手段将阻塞的操作变成非阻塞的操作,非阻塞的socket
一个进程的三种状态:1阻塞,2运行,3就绪
并发和串行
目前程序存在的问题
默认执行方式是串行,所谓串行,即程序自上而下运行,一行行代码执行,必须把当前任务执行完才进行下面的任务,不管花多长时间
e.g.
- 在tcp服务器中,如果正在进行通讯循环无法处理其他客户的要求
- 在硬盘中读取大文件
- 执行input
学习并发的目的
编写可以同时执行多个任务的程序,来提高效率
串行和并发都是程序处理任务的方式
多道技术是
实现原理:---------------------有了多道技术,计算机就可以同时并发处理多个任务
1,空间复用:
同一时间,加载多个任务到内存中,多个进程之间内存区域需要相互隔离,这个隔离是物理层面的隔离,目的是保证数据的安全
2,时间的复用:
操作系统会在多个进程之间按做切换执行,切换任务的两个情况
- 1当一个进程遇到了IO操作,会自动切换
- 2当一个任务执行时间超过阈值会强制i切换
在切换前必须保存状态,以便恢复执行----频繁切换是需要消耗资源的
当所有任务都没有io操作时,切换执行效率反而降低,为了保证并发执行,必须牺牲效率
进程
什么是进程
1,进程指的是正在运行的程序,是一系列过程的统称,也是操作系统在调度和进行资源分配的基本单位
2,进程怎么来的:当程序从硬盘读入内存,进程就产生了
3,多进程的概念:多个程序同一时间被装入内存并且执行(一个程序可以产生多个进程,比如说运行多个qq程序)
进程是实现并发的一种方式-------涉及到操作系统,因为这个概念来自于操作系统,没有操作系统就没有进程
- 多进程实现原理就是操作系统调度进程的原理
进程的创建和销毁
创建:
- 用户的交互式请求,鼠标双击
- 由一个正在运行的程序,调用了开启进程的接口,例如 subprosess
- 一个批作业的开始
- 系统的初始化
销毁:
- 任务完成,自愿退出
- 强制退出,taskkill kill(非自愿)
- 程序遇到了异常
- 严重的的错误,访问了不该访问的内存
PID和PPIFD
PID是当前进程的编号
PPID是父进程的编号
在运行py文件时其实是运行的python解释器
访问PID PPID
import os
os.getpid()
os.getppid()
python如何使用多进程
创建子进程的方式
1导入multiprocessing 中的Process类 实例化这个类,指定要执行的任务target
import os
from multiprocessing import Process
"""process 就表示进程"""
def task():
print("this is sub process")
print("sub process id %s"% os.getpid())
if __name__=='__main__'
"开启进程的代码必须是放在__main__判断下面"
#实例化一个进程对象,并制定他要做的事情,函数来指定
p=Process(target =task)
p.start() #给操作系统发送信息,让其开启进程
print("this is parent process")
print('parent process is %s'% os.getpid())
print('over')
widows 回到如父进程的代码,从头执行一遍,来获取需要处理的任务
所以在编写代码时如果是windows一定要开启进程的代码放在main判断中
linux可以不放
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())
3进程之间内存相互隔离
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)
4join函数
from multiprocessing import Process
import time
def task1(name):
for i in range(1000):
print("%s run"% name)
def task2(name):
for i in range(100):
print("%s run"% name)
if __name__=='__main__': #args 是给子进程传参的参数,必须是元组
p1=Process(target=task1,arg=("p1",))
p1.start() #向操作系统发送指令
#p1.join() #让主进程 等待子进程执行完毕再急促执行
p2 =Process(target=task2,args("p2",0))
p2.start()
p2.join()
p1.join()
#需要达到的效果是 必须保证两个子进程是并发执行的,并且over一定是在所有执行完毕后才执行的
print('over')
案例:join的使用
#join使用
from multiprocessing import Process
import time
def task1(name):
for i in range(10):
print("% run "% name)
if __name__=='__main__': #args 是给子进程传递额参数,必须是元组
ps =[]
for i in range(10):
p=Process(target=task1,arg=(i,))
p.start()
ps.append(p)
#换个join下
for i in ps:
i.join()
print('over')
进程对象的常用属性:
if__name__=='__main__':
p=Process(target=task,name="老司机进程")
p.start()
p.join()
print(p.name)
p.daemon #守护进程
print(p.exitcode) #获取进程的退出码 就是exit()函数中传入的值
print(p.is_alive())#查看进程是否存活
print('zi',p.pid) #获取进程id
print(os.getpid())
p.terminate() #终止进程 与strat 相同的是,不会立即终止,因为操作系统有很多事要做
僵尸进程和孤儿进程
孤儿进程:当父进程已经结束了,而子进程还在运行,子进程就变成了孤儿进程,尤其是存在的重要性,没有不良影响
僵尸进程:当一个进程结束了,它任然还有一些数据存在,此时称之为
在linux机制中,有这么一个机制,父进程无论什么时候都可以获取到子进程的一些数据
子进程任务完成后,确实结束了但是任然保留一些数据,目的是为了让父进程能够获取这些信息
linux中,可以调用waitpid来彻底清除子进程的残留信息
python中,已经封装处理了僵尸进程操作,无需关心