fork
在unix/linux操作系统中,提供了一个fork()系统函数,它非常特殊。
- 普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次。因为操作系统自动把当前进程(父进程)复制了一份(子进程),然后分别在父进程和子进程内返回。
- 子进程永远返回0,父进程返回子进程的ID。
- 一个父进程可以fork()出很多个子进程。因此,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的id。getpid()可以拿到当前进程id
- 父进程、子进程执行顺序没有规律,完全取决于操作系统的调度算法。
#只能在Linux和unix系统下运行
import os
pid=os.fork()
if pid<0:
print('fork失败')
elif pid==0: #子进程运行
print('子进程%s父进程%s'%(os.getpid(),os.getppid()))
else:#父进程运行
print('父进程%s子进程%s' % (os.getpid(), pid))
print('父子进程都可以执行')
py的多进程创建
由于py是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块提供了一个Process类来代表一个进程对象。
Process类:
构造函数中的参数:
- target:指定要绑定的函数,也就是进程创建后需要执行的函数
- name:给创建的进程起一个名字
- args:给target指定的函数传参,按照位置传参,需要给args传入数租
- kwargs:需要传入一个字典,给target指定的函数按照键值传参
常用属性:
- name:当前进程别名,默认为Process-N,N为从1开始递增的整数
- pid:当前进程实例的PID值。
常用方法:
- start(): 启动子进程
该句执行时才是正常的创建子进程,而不是Process时创建
- join([timeout]):回收子进程,等待子进程结束后在继续往下运行,通常用于进程间的同步
父进程阻塞等待子进程执行完毕,timeout设置最长的阻塞时间,如果操作该时间还没有子进程退出,则不在继续等待。
- p.daemon: 内核帮助应用层记录子进程的退出情况,当使用join函数时,内核会及时返回进程状态给应用层进行处理。
p.daemon #默认值False,功能让父进程运行结束后,不会影响子进程的运行直到等待子进程运行结束再一起退出。如果设置其值为True,则主进程运行完毕则所有子进程不再运行,一起退出。
p.daemon 该属性的设置需要在start()前设置。
该属性的设置并不是将进程设置为linux或unix 中的守护进程。 - is_alive():判断进程实例是否还在执行。
- run():如果没有给定target参数,对进程对象调用start()方法时,将执行对象中的run()方法。
- terminate():不管任务是否完成,立即终止。
from multiprocessing import Process
import os
#子进程执行代码
def run(name):
print('子进程运行中,name=%s,pid=%d'%(name,os.getpid()))
if __name__=='__main__':
print('父进程%d'%os.getpid())
#target为子进程需要执行的函数
p=Process(target=run,args=('test',))
print('子进程将执行')
p.start()
#等待子进程执行完父进程继续执行
p.join()
print('子进程执行结束')
from multiprocessing import Process
import os
#子进程执行代码
def run(name):
print('子进程运行中,name=%s,pid=%d'%(name,os.getpid()))
if __name__=='__main__':
print('父进程%d'%os.getpid())
p=Process(target=run,kwargs={'name':'test',})
print('子进程将执行')
#守护父进程,让子进程不在执行了。
p.daemon=True
p.start()
#等待子进程执行完父进程继续执行
#p.join()
print('子进程执行结束')
Process子类化
创建新的进程还可以使用类的方式,可以自定义一个类,继承Process类,每次实例化这个类的时候,就等同于实例化一个进程对象。我们需要做的就是重写run()方法,这点和java多线程的创建方式很像。
from multiprocessing import Process
import os
import time
class Process_Test(Process):
def __init__(self,interval):
Process.__init__(self)
self.interval=interval
#重写run方法
def run(self):
print('子进程%d,父进程%d'%(os.getpid(),os.getppid()))
t_start=time.time()
time.sleep(self.interval)
t_stop=time.time()
print('%s执行结束,耗时%0.2f'%(os.getpid(),t_stop-t_start))
if __name__=='__main__':
p=Process_Test(2)
p.start()
p.join()
print('主进程%s'%os.getpid())
'''
子进程1448,父进程11976
1448执行结束,耗时2.00
主进程11976
'''
进程池
当需要创建的子进程数量不多时,可以利用上面的Process动态生成多个进程,如果创建的进程多,可以使用multiprocessing模块提供的Pool方法。
初始化进程池Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池子没有满,那么就创建一个新的进程来执行该请求。如果池中进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来执行。
常用函数:
- apply_async(func[,args[,kwds]]):异步,使用非阻塞方式(父进程不阻塞)调用func(并行执行,阻塞方式必须等待上一个进程退出才能执行下一个进程),args为func参数列表,kwds为func的关键字参数列表
- apply(func[,args[,kwds]]):阻塞方式调用func(父进程阻塞)
- close():关闭pool,使其不在接受新任务。
- terminate():不管任务是否完成,立即终止。
- join():主进程阻塞,等待子进程的退出,必须在close/termanate之后使用。
通过下面例子我们可以看出,当创建出三个进程后,只有其中一个进程执行完毕,才会在创建一个新的进程。
from multiprocessing import Pool
import os,time,random
def worker(msg):
t_start=time.time()
print('%s开始执行,进程号%d'%(msg,os.getpid()))
time.sleep(random.random()*2)
t_stop=time.time()
print(msg,'执行完毕,耗时%0.2f'%(t_stop-t_start))
if __name__=='__main__':
pool = Pool(3) # 创建一个进程池,最大进程数为3
for i in range(10):
# 每次循环将会用空闲出来的子进程去调用目标
# 异步
pool.apply_async(worker, (i,))
print('--start--')
pool.close() # 关闭进程池
pool.join() # 等待进程池中所有子进程执行完毕,必须放在close()之后
print('--end--')
'''
#异步执行结果
--start--
0开始执行,进程号9244
1开始执行,进程号12652
2开始执行,进程号732
2 执行完毕,耗时0.26
3开始执行,进程号732
3 执行完毕,耗时0.35
4开始执行,进程号732
0 执行完毕,耗时0.80
5开始执行,进程号9244
1 执行完毕,耗时0.80
6开始执行,进程号12652
4 执行完毕,耗时0.25
7开始执行,进程号732
7 执行完毕,耗时0.40
8开始执行,进程号732
6 执行完毕,耗时1.74
9开始执行,进程号12652
8 执行完毕,耗时1.45
5 执行完毕,耗时1.99
9 执行完毕,耗时0.35
--end--
#同步执行结果
0开始执行,进程号6360
0 执行完毕,耗时0.47
1开始执行,进程号12684
1 执行完毕,耗时1.99
2开始执行,进程号8780
2 执行完毕,耗时1.17
3开始执行,进程号6360
3 执行完毕,耗时0.23
4开始执行,进程号12684
4 执行完毕,耗时1.01
5开始执行,进程号8780
5 执行完毕,耗时1.69
6开始执行,进程号6360
6 执行完毕,耗时0.31
7开始执行,进程号12684
7 执行完毕,耗时0.86
8开始执行,进程号8780
8 执行完毕,耗时0.66
9开始执行,进程号6360
9 执行完毕,耗时1.88
--start--
--end--
'''
消息队列
注意:
进程池使用消息队列的类和Process类使用的消息队列的类是不同的。
使用Process进程方式创建消息队列
from multiprocessing import Process,Pool,Queue
import os,time,random
def write(q):
for item in 'ABC':
print('写消息%s'%item)
q.put(item)
time.sleep(random.random())
def read(q):
while True:
if not q.empty():
item=q.get()
print('读消息%s'%item)
time.sleep(random.random())
else:
break
if __name__=='__main__':
q=Queue()
pw=Process(target=write,args=(q,))
pw.start()
pw.join()
pr=Process(target=read,args=(q,))
pr.start()
pr.join()
'''
写消息A
写消息B
写消息C
读消息A
读消息B
读消息C
'''
使用进程池方式创建消息队列
from multiprocessing import Manager,Pool
import os,time,random
def write(q):
for item in 'ABC':
print('写消息%s'%item)
q.put(item)
time.sleep(random.random())
def read(q):
while True:
if not q.empty():
item=q.get()
print('读消息%s'%item)
time.sleep(random.random())
else:
break
if __name__=='__main__':
#进程池使用消息队列,消息队列只能使用这一种
q=Manager().Queue()
pool=Pool(3)
#创建写进程
pool.apply(write,(q,))
#创建读进程
pool.apply(read,(q,))
pool.close()
pool.join()
'''
写消息A
写消息B
写消息C
读消息A
读消息B
读消息C
'''