本文首发于
知乎
python中的多进程编程方式和多线程非常相似,几乎可以说只是换了一些函数,有了之前讲过的多线程基础,很多地方我就只展示一些代码,在涉及到差别的地方再着重说明。
本文分为如下几个部分
- 事先说明
- 最简单的多进程
- 类的形式
- 进程池
- 进程之间内存独立
- 队列
- pipe
- value
- 进程锁
事先说明
有两点在写代码时需要注意
- 使用多进程时,最好在文件中编写代码,用cmd来执行,在jupyter经常无法得到想要的结果
-
创建进程的代码一定要放在
if __name__ == '__main__'
里面
最简单的多进程
import multiprocessing
import time
def myfun(num):
time.sleep(1)
print(num + 1)
if __name__ == '__main__':
for i in range(5):
p = multiprocessing.Process(target = myfun, args = (i, ))
p.start()
另外,
join is_alive daemon name current_process
等也都是一样的。
类的形式
import multiprocessing
import requests
from bs4 import BeautifulSoup
class MyProcess(multiprocessing.Process):
def __init__(self, i):
multiprocessing.Process.__init__(self)
self.i = i
def run(self):
url = 'https://movie.douban.com/top250?start={}&filter='.format(self.i*25
)
r = requests.get(url)
soup = BeautifulSoup(r.content, 'html.parser')
lis = soup.find('ol', class_='grid_view').find_all('li')
for li in lis:
title = li.find('span', class_="title").text
print(title)
if __name__ == '__main__':
for i in range(10):
p = MyProcess(i)
p.start()
进程池
import requests
from bs4 import BeautifulSoup
from multiprocessing import Pool, current_process
def get_title(i):
print('start', current_process().name)
title_list = []
url = 'https://movie.douban.com/top250?start={}&filter='.format(i*25)
r = requests.get(url)
soup = BeautifulSoup(r.content, 'html.parser')
lis = soup.find('ol', class_='grid_view').find_all('li')
for li in lis:
title = li.find('span', class_="title").text
# return title
title_list.append(title)
print(title)
return(title_list)
if __name__ == '__main__':
pool = Pool()
for i in range(10):
pool.apply_async(get_title, (i, ))
pool.close()
pool.join()
print('finish')
这里要说明一下
-
使用
Pool
时,不指定进程数量,则默认为CPU核心数量 - 核心数量对应电脑的(任务管理器-性能)逻辑处理器数量而不是内核数量(我的电脑2个内核,有4个逻辑处理器,所以这里默认使用4个进程)
-
进程数量可以是成百上千,并不是说最大开启进程数量为4,只要用
Pool(10)
就可以同时开启10个进程进行抓取 - 不过要注意一点,无论多线程还是多进程,数量开启太多都会造成切换费时,降低效率,所以慎重创建太多线程与进程
进程之间内存独立
多进程与多线程最大的不同在于,多进程的每一个进程都有一份变量的拷贝,进程之间的操作互不影响,我们先来看看下面的例子
import multiprocessing
import time
zero = 0
def change_zero():
global zero
for i in range(3):
zero = zero + 1
print(multiprocessing.current_process().name, zero)
if __name__ == '__main__':
p1 = multiprocessing.Process(target = change_zero)
p2 = multiprocessing.Process(target = change_zero)
p1.start()
p2.start()
p1.join()
p2.join()
print(zero)
运行结果如下
Process-1 1
Process-1 2
Process-1 3
Process-2 1
Process-2 2
Process-2 3
0
上面结果显示,新创建的两个进程各自把值增加到了3,二者不是一起将其加到了6的。同时,主进程的值还是0。所以说每个进程都是将数据拷贝过去自己做,并没有将结果与其他进程共享。
但是对于写入文件则不同
import