Python3学习笔记(一)——多进程运算计算圆周率π

我们使用公式

π28=n=0(12n+1)2=112+132+152+172+...\frac{\pi^2}{8}=\sum^\infty_{n=0}(\frac{1}{2n+1})^2=\frac{1}{1^2}+\frac{1}{3^2}+\frac{1}{5^2}+\frac{1}{7^2}+...

来近似计算圆周率π\pi的值。

一般的Python写法是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/env python3

import math, time

def pi(n):
s = 0.0
for i in range(n):
s += 1/((2*i+1)**2)
return math.sqrt(s * 8)

if __name__ == '__main__':
ticks = time.time()
print(pi(10000000))
print('Time consumed: {}. '.format(time.time()-ticks))

在笔者的设备打开系统监控,然后运行上述程序,情况如图所示

可以看到程序耗时约3.68秒,而在程序运行期间,只有一个CPU核心参与运算(见上图右侧界面中CPU History,可见只有一个CPU核心出现“高峰”)。

下面我们使用多进程进行并行运算,将pi(n)原本10000000规模的运算平均分为10份,分别交给10个进程去运算。程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/env python3

from multiprocessing import Pool, Queue
import os, math, time

def sub_task(left,right): #子任务,计算1/(2*left+1)^2到1/(2*right+1)的和
print('Range ({},{}), Process {} is running... '.format(left,right,os.getpid()))
s = 0.0
for i in range(left,right):
s += 1/((2*i+1)**2)
print('Range ({},{}) done, result is {}. '.format(left,right,s))
q.put(s) #将运算结果加入队列

def pi(n):
unit = n / 10 #计算任务均分10份
p = Pool() #建立进程池
for i in range(10): #调用10个子进程
p.apply_async(sub_task, args=(int(i*unit),int((i+1)*unit-1)))
print('{} had been appended. '.format(i))
print('Waiting for all processes done. ')
p.close() #关闭进程池
p.join() #等待进程池中的子进程全部运行完毕
print('All processes done. ')
s = 0.0
while not q.empty(): #将队列中的运算结果全部相加
s += q.get()
return math.sqrt(8 * s)

if __name__ == '__main__':
ticks = time.time()
q = Queue()
print(pi(10000000))
print('Time consumed: {}. '.format(time.time()-ticks))

在笔者的设备打开系统监控,然后运行上述程序,情况如图所示

可以看到程序耗时约1.98秒,而在程序运行期间,四个CPU核心全部参与运算(见上图右侧界面中CPU History,可见四个CPU核心全部出现“高峰”)。

可见,合理使用子进程,可以充分利用计算机运算资源,提高程序的总体运行效率。