并发编程之多进程
一 多进程
我们今天主要是进程的创建:
进程的创建分为四种:其中在进程中开启子进程是我们的重点。
1. 系统初始化(查看进程linux中用ps命令,windows中用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,
运行在后台并且只在需要时才唤醒的进程,称为守护进程,如电子邮件、web页面、新闻、打印) 2. 一个进程在运行过程中开启了子进程(如nginx开启多进程,os.fork,subprocess.Popen等) 3. 用户的交互式请求,而创建一个新进程(如用户双击暴风影音) 4. 一个批处理作业的初始化(只在大型机的批处理系统中应用)
无论哪一种,新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的:
1. 在UNIX中该系统调用是:fork,fork会创建一个与父进程一模一样的副本,二者有相同的存储映像、同样的环境字符串和同样的打开文件(在shell解释器进程中,执行一个命令就会创建一个子进程)
2. 在windows中该系统调用是:CreateProcess,CreateProcess既处理进程的创建,也负责把正确的程序装入新进程。
二 Process进程开启的两种方式:
开启子进程方式一: from multiprocessing import Process import time def task(name): time.sleep(2) print('%s is running'%name) if __name__ == '__main__': t=Process(target=task,args=('feng',))
t=Process(target=task,kwargs={'name':'feng'})#args与kwargs是一样的只是形式不同而已。
t.start()###### 向操作系统发送请求,操作系统会申请内存空间,然后把父进程的数据拷贝给子进程,位子进程的数据初始化。
print('zhu'.center(50,'*'))
运行结果为:
***********************zhu************************
feng is running
整个程序的运行其实就是,先定义一个函数task,然后运行主程序,在运行主程序的的过程创建一个子进程,子进程具体是由操作系统来创建的。而创建一个子程序所需要的时间远大于运行一行代码,所以首先打印出***********************zhu************************,然后子进程开始运行,输出feng is running
方式二: from multiprocessing import Process import time class MyProcess(Process): def __init__(self,name): super().__init__() self.name=name def run(self): ######这里必须是一个run方法,自己定义一个类,继承父类Process,
time.sleep(2)
print('%s is running'%self.name)
if __name__ == '__main__':
t=MyProcess('feng')
t.start() #t.start()调的就是run()
print('zhu'.center(50,'='))
运行结果为:
=======================zhu========================
feng is running
整个程序的运行,先定义一个类。
下面我们子进程与父进程的关系:
1 在内存中进程之间是互相隔离的:
from multiprocessing import Process import time x=1000 def run(name): global x x=0 time.sleep(2) print('%s is running'%name,x) if __name__ == '__main__': t=Process(target=run,args=('feng',)) t.start()#在这里子进程启动,其实就是调用父进程中的run,执行, time.sleep(5) print(x)
运行结果:
feng is running 0
1000
x=1000,是定义阶段就确定的,所以主进程的x不会随着在子进程中的改变而改变,而如果我们在子进程中打印x,那么就是改变后的值。
通过这个过程我们可以看出,子进程与父进程的内存空间是独立的。也就是说,子进程的改变不会影响父进程。父进程不会执行run内的代码。
2 Process对象的join方法 :就是让父进程等子进程运行结束再运行。
我们讲的jion的用法: from multiprocessing import Process import time x=1000 def task(): time.sleep(3) global x x=0 print('儿子结束了',x) if __name__ == '__main__': print(x) p=Process(target=task,) p.start() p.join() ##让父亲在原地等。等到子进程运行结束后父进程才会继续运行。 print(x) # 运行结果为: ''' 1000 儿子结束了 0 1000'''
3 Process子进程实现并发:
# from multiprocessing import Process # import time # # def task(n): # print('%s is running'%n) # if __name__ == '__main__': # for i in range(1,4): # p=Process(target=task,args=(i,)) # p.start() # # print('主'.center(20,'=')) # # 输出结果为: # ''' # =========主========== # 3 is running # 1 is running # 2 is running'''#为什么输出结果为无序的呢:那是因为start只是向操作系统发出请求,创建子进程,那么创建完子进程嗯么运行 #这完全是由操作系统控制的了。
4 join与并发:
方式一:比较low的方式
from multiprocessing import Process import time def task(n): print('%s is running'%n) time.sleep(3) if __name__ == '__main__': p1=Process(target=task,args=(1,)) p2 = Process(target=task, args=(2,)) p3 = Process(target=task, args=(3,)) p1.start() p2.start() p3.start() p1.join() p2.join() p3.join() print('主') # 运行结果为: ''' 2 is running 1 is running 3 is running
方式二:使用循环:
from multiprocessing import Process
import time
def task(n):
print('%s is running'%n)
time.sleep(3)
if __name__ == '__main__':
# start_time=time.time()
# p_list=[]
# for i in range(1,4):
# p=Process(target=task,args=(i,))
# p_list.append(p)
# p.start()
# for p in p_list:
# p.join()
# print('主',time.time()-start_time)
运行结果:
# '''
# 2 is running
# 1 is running
# 3 is running
# 主 2.1594531536102295
# '''
5 Process进程对象方法:terminate,is_alive。
进程对象的其他方法一:terminate,is_alive from multiprocessing import Process import time import random class Piao(Process): def __init__(self,name): self.name=name super().__init__() def run(self): print('%s is piaoing' %self.name) time.sleep(random.randrange(1,5)) print('%s is piao end' %self.name) p1=Piao('egon1') p1.start() p1.terminate()#关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活 print(p1.is_alive()) #结果为True print('开始') print(p1.is_alive()) #结果为False
6 查看进程id也就是pid
# from multiprocessing import Process # import time,random # import os # # def task(): # time.sleep(2) # print('me:%s,father:%s'%(os.getpid(),os.getppid())) # if __name__ == '__main__': # p2=Process(target=task,) # p2.start() # # p2.join() # # print(p2.name,p2.pid) # print('主',os.getppid())
print('主',os.getpid())#这个输出的结果是跟子进程的father是一样的。
print('主',os.getppid())#而这个输出的结果为pycha的pid。
# # 运行结果为: # ''' # Process-1 8200 # 主 3428 # me:8200,father:4940'''
僵尸进程:子进程死了,父进程还在(父进程大量造子进程,导致大量pid被占用),僵尸进程是有害的。
孤儿进程:父进程先结束,子进程由init来管理子进程,孤儿进程是无害的。