多进程
- 描述
由于Python中全部解释器锁(GIL)的存在,所以Python多线程并不能有效利用CPU多核的性能(相当于单核并发)实现多线程多核并行。
如果想充分地使用多核CPU的资源,需要使用多进程,Python中提供multiprocessing实现。
- multiprocessing
multiprocessing
模块就是跨平台版本的多进程模块,提供了一个Process
类来代表一个进程对象
语法:
multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={},*,daemon=None)
参数
group —— 始终是None
target —— 是由run()方法调用的可调用的对象,默认None
name —— 进程名称
args —— 目标调用的参数元组
kwargs —— 目标调用的关键字参数字典
daemon —— 是否为守护进程
方法
start(): 启动进程活动,并调用该子进程中的run()
run():进程启动时运行的方法,正是它去调用target指定的函数
is_alive():返回进程是否还活着
join([timeout]):如果可选参数timeout是None(默认值),则该方法将堵塞,直到调用join()方法的进程终止
通俗的讲哪个子进程调用了join方法,主进程就要等该子进程执行完成后才继续往下执行
例
from multiprocessing import Process
import os
# 子进程要执行的代码
def run_proc(name):
print('Run child process {} {}...'.format(name, os.getpid()))
# os.getpid()获取当前进程id
if __name__ == '__main__':
print('Rarent process {}'.format(os.getpid()))
p = Process(target=run_proc, args=('test',))
print('Child process will start')
p.start()
p.join()
print('Child process end')
输出结果
Parent process 14100
Child process will start
Run child process test 14128...
Child process end
- Pool
如果要启动大量的子进程,可以用进程池的方式批量创建子进程
from multiprocessing import Pool
import os
import time
import random
def long_time_task(name):
print('Run task {} {}...'.format(name, os.getpid()))
start = time.time()
time.sleep(random.random()*3)
end = time.time()
print('Task {} runs {:.2f} seconds'.format(name, (end-start)))
if __name__ == '__main__':
print('Parent process {}.'.format(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 6868.
Waiting for all subprocesses done...
Run task 0 3812...
Run task 1 6952...
Run task 2 11300...
Run task 3 7644...
Task 1 runs 0.33 seconds
Run task 4 6952...
Task 2 runs 0.60 seconds
Task 4 runs 1.73 seconds
Task 0 runs 2.06 seconds
Task 3 runs 2.46 seconds
All subprocesses done.
Pool
对象调用join()
方法会等待所有子进程执行完毕,调用join()
之前必须先调用close()
,调用close()
之后就不能继续添加新的Process了。
task0,1,2,3是立刻执行的,而task4要等待前面某个task完成后才执行。是因为p = Pool(4)
,最多同时执行4个进程。
Pool的默认大小是CPU的核数
- 进程间通信
Process之间是需要通信的。Python的multiprocessing模块包装了底层的机制,提供了Queue交换数据
例
from multiprocessing import Process, Queue
import os
import time
import random
# 写数据进程执行的代码
def write(q):
print('Process to write:{}'.format(os.getpid()))
for value in ['A','B','C']:
print('Put {} to queue...'.format(value))
q.put(value)
time.sleep(random.random())
# 读数据进行执行的代码
def read(q):
print('Process to read:{}'.format(os.getpid()))
while True:
value = q.get(True)
print('Get {} from queue'.format(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:13548
Put A to queue...
Process to read:8048
Get A from queue
Put B to queue...
Get B from queue
Put C to queue...
Get C from queue
- 知识拓展
并发:在一个时间段,处理多个任务,单核就可以实现并发
并行:在同一时刻,处理多个任务,必须多核才能并行