一、基本概念
fork()调用一次,返回两次,因为操作系统自动把当前进程复制了一份,然后分别在父子进程内返回。子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getpid()
就可以拿到父进程的ID。
二、multiprocessing
multiprocessing模块提供了一个Process类来代表一个进程对象,进程创建:Process(target=主要运行的函数,name=自定义进程名称可不写,args=(参数))。下面的例子演示了启动一个子进程并等待其结束:
from multiprocessing import Process
import os
# 子进程要执行的代码
def run_proc(name):
print('Run child process %s (%s)...' % (name, os.getpid()))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Process(target=run_proc, args=('test',))
print('Child process will start.')
p.start()
p.join()
print('Child process end.')
执行结果:
Parent process 51832.
Child process will start.
Run child process test (51972)...
Child process end.
其中:创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动。join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
is_alive() | 判断进程是否存活 |
join([timeout]) | 子进程结束再执行下一步,timeout为超时时间,有时进程遇到阻塞,为了程序能够运行下去而设置超时时间 |
run() | 如果在创建Process对象的时候不指定target,那么就会默认执行Process的run方法 |
start() | 启动进程,区分run() |
terminate() | 终止进程,关于终止进程没有这么简单,貌似用psutil包会更好,有机会以后了解更多再写下。 |
三、Pool
如果要启动大量的子进程,可以用进程池的方式批量创建子进程:
Pool(processes =num) | 设置运行进程数,当一个进程运行完,会添加新的进程进去 |
apply_async(函数,(参数)) | 非阻塞,其中参数是tulpe类型 |
apply(函数,(参数)) | 阻塞 |
close() | 关闭pool,不能再添加新的任务 |
terminate() | 结束运行的进程,不再处理未完成的任务 |
join() | 和Process介绍的作用一样, 但要在close或terminate之后使用 |
from multiprocessing import Pool
import os,time,random
def long_time_task(name):
print('Run task %s (%s)...'%(name,os.getpid()))
start = time.time() #返回当前时间的时间戳
time.sleep(random.random()*3) #返回随机生成的一个实数,它在[0,1)范围内。
end = time.time()
print('Task %s runs %0.2f seconds.'%(name,(end - start)))
if __name__ =='__main__':
print('Parent process %s.'%os.getpid())
p = Pool(4)
for i in range(5):
p.apply_async(long_time_task, args=(i,))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')
执行结果:
Parent process 39360.
Waiting for all subprocesses done...
Run task 0 (9312)...
Run task 1 (5680)...
Run task 2 (37780)...
Run task 3 (34748)...
Task 2 runs 0.57 seconds.
Run task 4 (37780)...
Task 3 runs 1.05 seconds.
Task 0 runs 1.10 seconds.
Task 1 runs 2.14 seconds.
Task 4 runs 2.06 seconds.
All subprocesses done.
其中需要特别注意的是:对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。请注意输出的结果,task 0,1,2,3是立刻执行的,而task 4要等待前面某个task完成后才执行,这是因为Pool的默认大小在我的电脑上是4,因此,最多同时执行4个进程。这是Pool有意设计的限制,并不是操作系统的限制。
三、进程间通信
Process之间肯定是需要进行通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。
1、Queue
from multiprocessing import Process,Queue
import os,time,random
#写数据进程执行的代码:
def write(q):
print('Process to write: %s'%(os.getpid()))
for value in ['A','B','C']:
print('Put %s to queue...'%(value))
q.put(value)
time.sleep(random.random())
#读数据进程执行的代码:
def read(q):
print('Process to read:%s'%(os.getpid()))
while True:
value = q.get(True)
print('Get %s from queue.'%(value))
if __name__ == '__main__':
# 父进程创建queue,并传给子进程:
q = Queue()
pw = Process(target = write ,args= (q,))
pr = Process(target = read, args= (q,))
#启动子进程pw,写入:
pw.start()
#启动子进程pr,读取:
pr.start()
#等待pw结束:
pw.join()
#pr进程里是死循环。无法等待其结束,只能强行终止:
pr.terminate()
显示结果:
Process to write: 51344
Put A to queue...
Process to read:52252
Get A from queue.
Put B to queue...
Get B from queue.
Put C to queue...
Get C from queue.