Python多进程编程:从基础到实战!
在多核CPU时代,充分利用多进程并行处理器能够有效提高程序的运行效率。Python作为一种强大的编程语言,也提供了方便好用的多进程编程模块,本文将从基础的多进程概念开始,逐步带领读者进入实战级别的多进程编程。
一、多进程的概念
在计算机中,进程是指一个正在执行的程序的实例。一个进程可以包含多个线程,在现代操作系统中,常常会有多个进程在同时运行。每个进程都拥有自己独立的地址空间、内存等资源,并且进程之间不能直接通信,需要借助特殊的通信机制,比如管道、消息队列等。多进程就是指一个程序或者一个任务被分割成多个进程同时运行的方式。
二、Python多进程模块介绍
在Python中,提供了多个多进程编程模块,其中最常用的是multiprocessing模块。下面是multiprocessing模块中常用的几个类和函数:
1. Process类:表示一个进程对象,可以通过这个类启动新的进程。
2. Pool类:表示进程池对象,可以用来管理一组进程,重复利用进程对象,避免反复创建和销毁进程对象的开销。
3. Queue类:表示一个队列对象,可以用来在多进程之间传递消息。
4. Pipe类:表示两个进程之间的管道,可以用来进行进程间通信。
5. Lock类、RLock类、Semaphore类:这些类提供了一些进程同步机制,用来处理多个进程之间的竞争问题。
6. Value类、Array类:这些类提供了一些共享内存的方式,可以让多个进程之间共享一块内存区域。
三、Python多进程编程基础
1. Process类的使用
使用Process类,可以创建一个新的进程对象,然后通过调用start()方法,启动进程。例如:
```python
import multiprocessing
def worker():
print('Worker start')
print('Worker end')
if __name__ == '__main__':
p = multiprocessing.Process(target=worker)
p.start()
```
在这个例子中,创建了一个worker函数,该函数打印一条开始和结束信息。然后使用Process类创建了一个新的进程对象p,并通过target参数指定使用worker函数作为进程的执行函数。最后通过调用p.start()方法启动进程。
2. 进程间通信
由于每个进程都是独立的,进程之间不能直接共享数据。但是,Python提供了多种通信机制,可以让进程之间进行数据交换。常见的通信机制包括:Queue、Pipe、Manager等。Queue和Pipe的用法和Python中的标准库Queue和Pipe类似,这里不再赘述,下面简单介绍一下Manager类的用法。
Manager类提供了一种简单的方式来创建并管理共享的对象。例如:
```python
import multiprocessing
def worker(d):
print(f'Worker start, d={d[0]}')
d[0] += 1
if __name__ == '__main__':
mgr = multiprocessing.Manager()
d = mgr.list([0])
p = multiprocessing.Process(target=worker, args=(d,))
p.start()
p.join()
print(f'd={d[0]}')
```
在这个例子中,使用Manager类创建了一个共享的list对象d。然后创建了一个进程对象p,并通过args参数将d传递给进程。在进程中通过修改d[0]的值来间接的修改共享的数据。在主进程中,打印d的值,可以看到d的值已经被修改。
3. 进程池
如果需要并行运行多个相同的任务,可以使用进程池,重复利用进程,避免开销。例如:
```python
import multiprocessing
import time
def worker(n):
print(f'Worker start, n={n}')
time.sleep(2)
print(f'Worker end, n={n}')
if __name__ == '__main__':
pool = multiprocessing.Pool()
for i in range(5):
pool.apply_async(worker, args=(i,))
pool.close()
pool.join()
```
在这个例子中,使用Pool类创建一个进程池对象,并通过apply_async方法向进程池提交了5个任务。每个任务都执行了worker函数,打印了开始和结束信息。在创建完任务之后,调用进程池的close方法,表示不会再向进程池中添加任务。最后调用进程池的join方法等待所有任务结束。
四、Python多进程编程实战
1. 多进程并行下载
在实现多进程并行下载时,可以使用进程池,每个进程处理一个单独的下载任务。例如:
```python
import requests
import multiprocessing
import os
def download(url, path):
with open(path, 'wb') as f:
r = requests.get(url, stream=True)
total_length = int(r.headers.get('content-length'))
for chunk in r.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
print(f'{os.getpid()} download {url}')
if __name__ == '__main__':
urls = [
'https://images.pexels.com/photos/3801957/pexels-photo-3801957.jpeg',
'https://images.pexels.com/photos/3823483/pexels-photo-3823483.jpeg',
'https://images.pexels.com/photos/3739124/pexels-photo-3739124.jpeg',
'https://images.pexels.com/photos/3758083/pexels-photo-3758083.jpeg',
'https://images.pexels.com/photos/3823479/pexels-photo-3823479.jpeg',
'https://images.pexels.com/photos/3827826/pexels-photo-3827826.jpeg',
'https://images.pexels.com/photos/3771272/pexels-photo-3771272.jpeg',
'https://images.pexels.com/photos/3787374/pexels-photo-3787374.jpeg',
'https://images.pexels.com/photos/3720546/pexels-photo-3720546.jpeg',
'https://images.pexels.com/photos/3731065/pexels-photo-3731065.jpeg',
]
pool = multiprocessing.Pool()
for i, url in enumerate(urls):
pool.apply_async(download, args=(url, f'pic/{i}.jpg'))
pool.close()
pool.join()
```
在这个例子中,首先定义了一个download函数,用来下载指定的url并保存到指定的文件路径中。然后定义了一个urls列表,该列表包含了需要下载的url列表。接着,创建了一个进程池对象,并通过apply_async方法向进程池中添加了多个任务,每个任务都是执行download函数来下载指定的url。最后调用进程池的close方法表示不再向进程池中添加任务,然后调用进程池的join方法等待所有任务完成。
2. 并行计算PI值
计算PI值是一个很耗时的计算任务,使用多进程并行计算可以有效提高计算速度。例如:
```python
import multiprocessing
import decimal
def calc_pi(start, step):
sum = decimal.Decimal(0)
for i in range(start, start + step):
sum += pow(-1, i) / (decimal.Decimal(2 * i + 1))
return sum
if __name__ == '__main__':
decimal.getcontext().prec = 100000
num_steps = 1000000
step_size = 50
pool = multiprocessing.Pool()
results = []
for i in range(0, num_steps, step_size):
results.append(pool.apply_async(calc_pi, args=(i, step_size)))
pool.close()
pool.join()
pi = decimal.Decimal(0)
for r in results:
pi += r.get()
print(pi * 4)
```
在这个例子中,首先设置了decimal模块的精度为100000。然后定义了一个calc_pi函数,该函数用来计算指定的start和step范围内的PI值。接着,定义了num_steps和step_size两个变量,表示需要计算的PI值范围和每一次计算的步长。创建了一个进程池对象,并通过apply_async方法向进程池中添加了多个任务,每个任务都是执行calc_pi函数来计算指定范围的PI值。然后调用进程池的close方法表示不再向进程池中添加任务,然后调用进程池的join方法等待所有任务完成。最后,将所有的PI值累加起来,乘以4,得到最后的PI值。
以上是Python多进程编程的基础和实战内容,多进程编程是Python中比较常用和重要的一部分,掌握多进程编程技能可以大幅提高程序运行效率。