一、进程的相关理论
1、什么是线程?
进程:正在进行的一个过程或者说一个任务(如运行一个应用程序QQ)。而负责执行任务则是cpu。
2、进程与程序的区别?
程序仅仅只是一串串代码而已,而进程指的是程序的运行过程。
注意:同一个程序执行两次,那也是两个进程,比如打开暴风影音,虽然都是同一个软件,但是一个可以播放《大秦帝国》,
一个可以播放《西西里的美丽传说》。
3、并发与并行
3.1 无论是并行还是并发,在用户看来都是'同时'运行的,不管是进程还是线程,都只是一个任务而已,真正干活的是cpu,
cpu来做这些任务,而一个cpu同一时刻只能执行一个任务。
3.2 并发:是伪并行,即看起来是同时运行。单个cpu+多道技术就可以实现并发(单核+多道,实现多个进程的并发执行)。
比如:你要做作业,想打游戏,还想散步,聊QQ。但你同一时刻只能做一件事,如何才能玩出多个任务并发执行的效果?
可以这样:你先做会儿作业,再打会儿游戏,再去散步,再和别人聊QQ。如此,你就能在一段时间里做各种事情了(感觉没什么意义...)
3.3 并行:同时运行,只有具备多个cpu才能实现并行,单核下,可以利用多道技术,多个核,
每个核也都可以利用多道技术(多道技术是针对单核而言的)。
比如:有四个核,六个任务,这样同一时间有四个任务被执行,假设分别被分配给了cpu1,cpu2,cpu3,cpu4。
4、进程的创建
对于通用系统(跑很多应用程序,如我们常用的Windows系统),需要有系统运行过程中创建或撤销进程的能力,主要分为4中形式创建新的进程
- 系统初始化(查看进程linux中用ps命令,windows中用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台并且只在需要时才唤醒的进程,称为守护进程,如电子邮件、web页面、新闻、打印)
- 一个进程在运行过程中开启了子进程(如subprocess.Popen等)
- 用户的交互式请求,而创建一个新进程(如用户双击暴风影音)
- 一个批处理作业的初始化(只在大型机的批处理系统中应用)
无论哪一种,新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的:
- 在UNIX中该系统调用是:fork,fork会创建一个与父进程一模一样的副本,二者有相同的存储映像、同样的环境字符串和同样的打开文件(在shell解释器进程中,执行一个命令就会创建一个子进程)
- 在windows中该系统调用是:CreateProcess,CreateProcess既处理进程的创建,也负责把正确的程序装入新进程。
5、关于创建的子进程,UNIX和windows
1.相同的是:进程创建后,父进程和子进程有各自不同的地址空间(多道技术要求物理层面实现进程之间内存的隔离),任何一个进程的在其地址空间中的修改都不会影响到另外一个进程。
2.不同的是:在UNIX中,子进程的初始地址空间是父进程的一个副本,提示:子进程和父进程是可以有只读的共享内存区的。但是对于windows系统来说,从一开始父进程与子进程的地址空间就是不同的。
6、进程的终止
- 正常退出(自愿,如用户点击交互式页面的叉号,或程序执行完毕调用发起系统调用正常退出,在linux中用exit,在windows中用ExitProcess)
- 出错退出(自愿,python a.py中a.py不存在)
- 严重错误(非自愿,执行非法指令,如引用不存在的内存,1/0等,可以捕捉异常,try...except...)
- 被其他进程杀死(非自愿,如kill -9)
7、进程的层次结构
无论UNIX还是windows,进程只有一个父进程,不同的是:
- 在UNIX中所有的进程,都是以init进程为根,组成树形结构。父子进程共同组成一个进程组,这样,当从键盘发出一个信号时,该信号被送给当前与键盘相关的进程组中的所有成员。
- 在windows中,没有进程层次的概念,所有的进程都是地位相同的,唯一类似于进程层次的暗示,是在创建进程时,父进程得到一个特别的令牌(称为句柄),该句柄可以用来控制子进程,但是父进程有权把该句柄传给其他子进程,这样就没有层次了。
8、进程的状态
tail -f access.log |grep '404'
执行程序tail,开启一个子进程,执行程序grep,开启另外一个子进程,两个进程之间基于管道'|'通讯,将tail的结果作为grep的输入。
进程grep在等待输入(即I/O)时的状态称为阻塞,此时grep命令都无法运行
在两种情况下会导致一个进程在逻辑上不能运行:
1、进程挂起是自身原因,遇到I/O阻塞,便要让出CPU让其他进程去执行,这样保证CPU一直在工作。
2、与进程无关,是操作系统层面,可能会因为一个进程占用时间过多,或者优先级等原因,而调用其他的进程去使用CPU。
故而,一个进程有以下三种状态:
二、开启进程的方式
1、multiprocessing模块介绍
python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu\_count\(\)查看),在python中大部分情况需要使用多进程。
Python提供了multiprocessing。 multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),
该模块与多线程模块threading的编程接口类似。multiprocessing模块的功能众多:支持子进程、通信和共享数据、
执行不同形式的同步,>提供了Process、Queue、Pipe、Lock等组件。
2、Process类的介绍
创建进程的类:
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,可用来开启一个子进程
强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
参数说明:
group参数未使用,值始终为None
target表示调用对象,即子进程要执行的任务
args表示调用对象的位置参数元组,args=(1,2,'egon',)
kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
name为子进程的名称
方法介绍:
p.start():启动进程,并调用该子进程中的p.run()
p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
p.is_alive():如果p仍然运行,返回True
p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间。
属性介绍:
p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
p.name:进程的名称
p.pid:进程的pid
3、Process类的使用
注意:在windows中Process()必须放到# if __name__ == '__main__':下。
方式一:
from multiprocessing import Process
import time
def task(name):
print('%s is sleeping'%name)
time.sleep(3)
print('%s is awaked'%name)
if __name__ == '__main__':
p = Process(target=task,args=('子进程1',)) #括号里的逗号不可少
p.start() #仅仅是给操作系统发送了一个信号
print('主进程')
'''
'''运行结果
主进程
子进程1 is sleeping
子进程1 is awaked
'''
方式二:
from multiprocessing import Process
import time
class MyProcess(Process):
def __init__(self,name):
super().__init__()
self.name = name
def run(self):
'''默认就是run方法'''
print('%s is runing'%self.name)
time.sleep(3)
print('%s is walking'%self.name)
if __name__ == '__main__':
p2 = MyProcess('进程1')
p2.start()
print('主进程')
'''运行结果
主进程
进程1 is runing
进程1 is walking
'''