程序与进程
程序:可以被操作系统加载到内存中执行的一段代码;
进程:可执行程序被加载到内存中执行的整个过程;
由上面的区别可以看出程序相对与进程来讲是一个静态的概念,它是没有生命的,仅仅占用硬盘存储空间而已。进程相当于程序来讲是有生命的,在被执行的整个期间会占用内存的空间,还可能使用各种外部设备(键盘、鼠标等),整个运行期间还要受操作系统的调度。
多任务(进程)和CPU
操作系统都是多任务的,即可以在同一时刻执行多个进程(应用程序),比如我们可以在浏览网页的同时听音乐。实际上CPU本身在同一时刻只能做一件事,而我们本身所感受到的多任务的状态是因为操作系统的调度算法所呈现出来的,如常见的:时间片轮询法。操作系统把需要执行的任务在按照一定的顺序依次送给CPU执行,CPU对每次送过来的任务只执行很短的时间(微妙为单位),然后切换到下一个任务再次执行一定的时间,再次切换....。因为时间片很短,而CPU执行的很快,因此我们外部感知到的就是多任务在同时进行。
为什么要有多进程呢?因为一个应用程序本身可能不是单进程的,比如我们在微信上需要下载对方发过来的某个文件,而这个文件很大,如果不采用多进程的实现方式那么在整个下载过程中我们只能等待,不能进行其他操作。这样的话用户体验想必是极差的。
multiprocessing模块Process类
python是跨平台的,内置的multiprocessing模块中的Process类封装了实现多进程的相关操作,提供了和操作系统无关的进程实现机制。下面来看用这个模块实现的多进程例子:
#coding=utf-8
from multiprocessing import Process
import time
import os
def new_process(*args):
print(args)
print('this is new process,child process id:%d' % os.getpid())
i = 0
while True:
if i < 3:
print('in new process ,i = ',i)
i += 1
time.sleep(1)
else:
break
if __name__ == '__main__':
newP = Process(target=new_process,args =(1,2.2,True,'hello world'))
newP.start()
print('this is main process,main process id:',os.getpid())
k = 0
while True:
if k < 3:
print('in main process ,k = ',k)
k += 1
time.sleep(1)
else:
break
结果:
this is main process,main process id: 7784
in main process ,k = 0
(1, 2.2, True, 'hello world')
this is new process,child process id:1916
in new process ,i = 0
in main process ,k = 1
in new process ,i = 1
in main process ,k = 2
in new process ,i = 2
首先需要从multiprocessing 模块中导入Process类,接下来是new_process函数的定义,接下来就是创建Process的实例,这个对象至少需要一个关键字参数(target),target指向的是将来新创建出的进程要执行的那段代码,后面的参数args是函数target实际执行需要的参数,它也是以关键字参数的形式存在,并且写到一个元组中。变量newP指向新创建的对象,并调用这个对象的run方法,然后新创建的进程就开始执行了。与此同时主进程并没有停止等待子进程执行结束,同样是进行主进程接下来的操作,因此实际看到的打印结果是i和k是交叉打印的。
整个程序还导入了os模块,调用了os的getpid()方法,输出的结果是当前进程的ID。因为操作系统需要管理调度所有的进程,因此需要唯一的一个标识来区分每一个进程,ID就是那个唯一区分的标识。
join方法
阻塞当前进程,等待所创建的这个子进程执行完毕再往下接着执行。在上面的例子中如果把主进程中循环去掉结果就是下面的了。
#coding=utf-8
from multiprocessing import Process
import time
import os
def new_process(*args):
print(args)
print('this is new process,child process id:%d' % os.getpid())
i = 0
while True:
if i < 5:
print('in new process ,i = ',i)
i += 1
time.sleep(1)
else:
break
print('child process over!!!')
if __name__ == '__main__':
newP = Process(target=new_process,args =(1,2.2,True,'hello world',[1.1,2.2,True]))
newP.start()
print('this is main process,main process id:',os.getpid())
k = 0
# while True:
# if k < 3:
# print('in main process ,k = ',k)
# k += 1
# time.sleep(1)
# else:
# break
print('main process over!!!')
结果:
this is main process,main process id: 6704
main process over!!!
(1, 2.2, True, 'hello world', [1.1, 2.2, True])
this is new process,child process id:7332
in new process ,i = 0
in new process ,i = 1
in new process ,i = 2
in new process ,i = 3
in new process ,i = 4
child process over!!!
可以看到主进程先于子进程结束了,如果想要主进程的部分代码在子进程执行完后再执行可以调用join方法使子进程阻塞。这个方法默认是等待子进程执行完后才往下进行主进程的代码的。这个函数还能加一个参数,表示阻塞多长时间。如果已经到了阻塞的时间而子进程还没执行完,便不再等待。
#coding=utf-8
from multiprocessing import Process
import time
import os
def new_process(*args):
print(args)
print('this is new process,child process id:%d' % os.getpid())
i = 0
while True:
if i < 5:
print('in new process ,i = ',i)
i += 1
time.sleep(1)
else:
break
print('child process over!!!')
if __name__ == '__main__':
newP = Process(target=new_process,args =(1,2.2,True,'hello world',[1.1,2.2,True]))
newP.start()
print('this is main process,main process id:',os.getpid())
k = 0
# while True:
# if k < 3:
# print('in main process ,k = ',k)
# k += 1
# time.sleep(1)
# else:
# break
newP.join()
#newP.join(1)
print('main process over!!!')
结果:
this is main process,main process id: 1632
(1, 2.2, True, 'hello world', [1.1, 2.2, True])
this is new process,child process id:11988
in new process ,i = 0
in new process ,i = 1
in new process ,i = 2
in new process ,i = 3
in new process ,i = 4
child process over!!!
main process over!!!
this is main process,main process id: 10016
(1, 2.2, True, 'hello world', [1.1, 2.2, True])
this is new process,child process id:6584
in new process ,i = 0
main process over!!!
in new process ,i = 1
in new process ,i = 2
in new process ,i = 3
in new process ,i = 4
child process over!!!
terminate()
强制退出某个进程。如果主进程发生了某些不可逆的错误需要结束,那么所创建的所有子进程也应该结束,terminate()方法就能够实现这个操作。
#coding=utf-8
from multiprocessing import Process
import time
import os
def new_process(*args):
print(args)
print('this is new process,child process id:%d' % os.getpid())
i = 0
while True:
if i < 5:
print('in new process ,i = ',i)
i += 1
time.sleep(1)
else:
break
print('child process over!!!')
if __name__ == '__main__':
newP = Process(target=new_process,args =(1,2.2,True,'hello world',[1.1,2.2,True]))
newP.start()
print('this is main process,main process id:',os.getpid())
k = 0
# while True:
# if k < 3:
# print('in main process ,k = ',k)
# k += 1
# time.sleep(1)
# else:
# break
newP.join(1)
time.sleep(1)
print('\t\tchild process will exit!!!')
newP.terminate()
print('main process over!!!')
结果:
this is main process,main process id: 12104
(1, 2.2, True, 'hello world', [1.1, 2.2, True])
this is new process,child process id:11184
in new process ,i = 0
in new process ,i = 1
child process will exit!!!
main process over!!!
child process over!!! 没有输出,由此可见子进程被强制终止了。
Process的子类
Process是一个类,提供了进程实现的方法,那么我们自己实现一个类继承Process便同样可以实现多进程。最重要的是重写run方法。
from multiprocessing import Process
import time
class MyProcess(Process):
def run(self):
print('in child run function!!!')
for i in range(3):
print('in child:%d'% i)
print('in child run ,over!!!')
if __name__ == '__main__':
print('this is main Process')
time.sleep(1)
p = MyProcess()
p.start()
print('\t\tmain process doing。。。。。。。。。')
time.sleep(1)
print('this is main Process,over!')
结果:
this is main Process
main process doing。。。。。。。。。
in child run function!!!
in child:0
in child:1
in child:2
in child run ,over!!!
this is main Process,over!
进程池Pool
最后一种实现进程的方式是进程池,它是最简单的使用进程的方式。看下面的例子:
#coding=utf-8
from multiprocessing import Pool
import os
import time
def worker(cur):
for i in range(2):
print('********pid:%d cur:%d******'% (os.getpid(),cur))
time.sleep(1)
if __name__ == '__main__':
pool = Pool(3)
#创建进程池,3表示当前进程池中可同时进行的任务数量
for i in range(10):
pool.apply_async(worker,(i,))
#依次加入10个任务,()中写任务需要的参数
print('\t\t add jobs success!!!')
time.sleep(1)
pool.close()
#关闭进程池,便不能再向里面添加任务
pool.join()
#默认主进程结束,进程池中的任务就终止的,因此需要让主进程阻塞在这个地方
print('\n进程池中的任务结束了')
print('\n\t主进程将要结束')
time.sleep(2)
print('主进程over!')
结果:
add jobs success!!!
********pid:9728 cur:0******
********pid:5604 cur:1******
********pid:1432 cur:2******
********pid:9728 cur:0******
********pid:5604 cur:1******
********pid:1432 cur:2******
********pid:9728 cur:3******
********pid:5604 cur:4******
********pid:1432 cur:5******
********pid:9728 cur:3******
********pid:5604 cur:4******
********pid:1432 cur:5******
********pid:9728 cur:6******
********pid:5604 cur:7******
********pid:1432 cur:8******
********pid:9728 cur:6******
********pid:5604 cur:7******
********pid:1432 cur:8******
********pid:9728 cur:9******
********pid:9728 cur:9******
进程池中的任务结束了
主进程将要结束
主进程over!
代码中Pool(3)表示创建一个进程池对象,里面最多可以同时运行3个进程。apply_async(worker,(i,)) 是往进程池中增加任务,每个任务的参数以元组的形式传递。最后调用close()方法,使无法再向进程池中增加任务。在前两个例子中我们通过结果发现如果主进程先于子进程结束,子进程并不会结束,而Pool创建的子进程会随着主进程结束而结束,因此代码中调用了join方法,使主进程等待子进程结束。
需要注意的是:
Pool(3)的意思是可允许三个进程同时进程,但并不影响往里面添加任务队列。使用进程池的好处时它在一开始预先分配了资源用以执行进程,如果你想往里面添加任务,可以随时添加,整个程序结束后进程池的资源才会回收。因此相比前两种方式省去了不断创建进程和销毁进程的时间,是相对来讲比较高效的。但是预先申请的资源并不是越多越好,这一点要牢记。