进程:正在进行的一个过程或者说一个任务。
而负责执行任务的则是cpu。
进程由三部分组成:
代码段,数据段,PCB(进程控制块)
进程的三个基本状态:
就绪状态:获得了除CPU之外运行需要的所有资源
执行状态:获得了所有资源,处于正在执行的状态
阻塞状态:因为各种原因,导致进程放弃了cpu,导致进程无法继续执行,此时进程处于内存
程序仅仅只是一堆代码而已,而进程指的是程序的运行过程。 也可以说一个程序运行起来后,代码+用到的资源就可称之为进程,它是操作系统分配资源的基本单元。
1. 一个程序至少有一个进程,一个进程至少有一个线程;
2. 线程的划分尺度小于进程,使得多线程程序的并发性高;
3. 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率;
4. 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制;
5. 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看作多个独立的应用,来实现进程的调度和管理以及资源分配。
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程则相反。
可以说进程的创建和线程很相似,线程中使用threading模块里面的Thread类创建出实例对象, 然后通过start()方法真正的去产生一个新的线程。而进程则使用multiprocessing模块,这是一个跨平台版本的多进程模块,提供了一个Process类来代表一个进程对象,这个对象可以理解为是一个独立的进程,可以执行另外的事情,然后也是通过start()方法真正的去产生一个新的进程。
备用知识点:
查看当前进程:
import os
os.getpid()
查看当前进程父进程:
import os
os.getppid()
开启子进程的方式:
1 p = Process(target=func,args=(,))
target: 是子进程要执行的任务
args:是父进程给子进程传递的参数
2 自定义类,去继承Process
进程的方法:
p.start()开启子进程,底层调用的是p.run()
进程的常用方法:
(1) start() 开启一个子进程
(2) join() 异步变同步(就是让父进程停留在join这句话,等待子进程执行结束,父进程再继续执行)
(3) is_alive() 判断进程是否活着
(4) terminate() 杀死进程
进程的常用属性
(1) p.name = 给p进程一个名字
(2) p.pid 返回p进程的pid
(3) p.daemon = True 将p进程设置为守护进程。(True为守护进程,False为普通进程)
守护进程的两个特点:
守护进程会随着父进程的结束而结束
守护进程不能再创建子进程(不能要孩子)
不仅可以通过线程完成多任务,进程也是可以的,与线程协程比较,进程可以说是最稳定的,但是比较吃cpu。下面举个多进程完成多任务的例子:
import multiprocessing
import time
# 通过一个进程号去判断
import os
# 多进程完成多任务
def sing():
for i in range(1,4):
print("我正在唱第%s首歌" % i)
# 查看当前进程的父进程
print("进程在sing中运行, 进程号pid为:%s,父进程为%s" % (os.getpid(),os.getppid())) # 获取当前进程号
time.sleep(1.5)
def dance():
for i in range(1,4):
print("我正在跳第%s支舞" % i)
print("进程在dance中运行, 进程号pid为:%s,父进程为%s" % (os.getpid(),os.getppid())) # 获取当前进程号
time.sleep(1.5)
def main():
print("进程在main中运行, 进程号pid为:", os.getpid()) # 获取当前进程号
p1 = multiprocessing.Process(target = sing)
p2 = multiprocessing.Process(target = dance)
p1.start()
p2.start()
time.sleep(5)
print("进程在main中运行, 进程号pid为:", os.getpid()) # 获取当前进程号
if __name__ == '__main__':
main()
和线程传参基本一样,程序示例:
import multiprocessing
def demo1(*args,**kwargs):
print(args)
print(kwargs)
def main():
p1 = multiprocessing.Process(target=demo1,args=(1,2,3),kwargs={'a':1,"b":2})
p1.start()
if __name__ == '__main__':
main()
证实进程不共享全局变量:
import multiprocessing
import time
list1 = [1,2,3]
def demo1():
list1.append(4)
print("在demo1中,list1的值是:",str(list1))
def demo2():
list1.append(5)
print("在demo2中,list1的值是:",str(list1))
def main():
p1 = multiprocessing.Process(target=demo1)
p2 = multiprocessing.Process(target=demo2)
p1.start()
# time.sleep(1)
# join,和sleep一样,达到进程等待阻塞的效果,但是由于进程所占用的资源比较庞大,
# 我们并不确定p1具体需要多长时间才能执行完,所以sleep的时间无法确定,
# 相应的join方法就应运而生了,这个不需要具体的时间就能达到进程等待阻塞的效果
p1.join() # 还可以传参,比如p1.join(5) 设置超时等待,超过5秒释放资源程序继续往下执行
p2.start()
if __name__ == '__main__':
main()
time.sleep(3)
list1.append(6)
print("在主进程中,list1的值是:",str(list1))
程序执行结果:
理论上如果进程共享全局变量,在demo1中会有list1 = [1,2,3,4],在demo2中会有list1 = [1,2,3,4,5],在主进程中会有list1 = [1,2,3,4,5,6],然而我可以清楚的看到执行结果并不像我们预料的那样,实际上它的每一个进程对全局变量的操作互不干扰,所以可以证实进程是不共享全局变量的。