1. 多进程概念
(1)一个应用程序默认只有一个进程,一个进程中默认有一个线程。多进程指的是一个应用程序有多个进程
(2)如果所有的任务是相同的任务就直接用一个进程中添加多个线程的方案来解决问题
例如:如果要加工1000个玩具:一个进程,多个线程
(3)如果所有的任务可以分成两种或多种任务,可以每一种任务对应一个进程
例如:如果要加工800个玩具和400个蛋糕:两个进程,每个进程中多个线程
(4)如果希望应用程序中除了主进程以外有别的进程(子进程),就需要在程序中创建进程对象
RIGHT Example:
from time import sleep
from multiprocessing import Process, current_process
from threading import Thread, current_thread
def func1(x, y):
sleep(1)
print('子进程', current_process(), current_thread())
# 在这个函数中创建的线程就是子进程中的线程
t2 = Thread(target=func3)
t2.start()
def func2():
print('func2', current_process(), current_thread())
def func3():
print('func3', current_process(), current_thread())
if __name__ == '__main__':
print('主进程', current_process(), current_thread())
# 1. 创建进程对象
p = Process(target=func1, args=(10, 20))
# 2. 启动线程
p.start()
# 3. 阻塞
p.join()
print('----------------完成------------------')
2. 多进程数据通信
2.1 进程队列概念
不同进程中的数据不能直接共享。在一个进程中定义的全局变量不能在另外的进程中使用。如果想要让一个或者多个数据在不同进程中进行传递,只有一个方法:使用进程队列
2.2 进程队列的使用
RIGHT Example:
from threading import Thread
from multiprocessing import Process, Queue
def func2(qt: Queue):
print('子进程:', qt.get())
qt.put('哈哈哈')
if __name__ == '__main__':
# (1)创建队列对象
q = Queue()
# (2)
b = 100
q.put(b)
# (3)将队列对象以参数的形式传递到子进程对应的函数中
p1 = Process(target=func2, args=(q,))
p1.start()
p1.join()
# (4)获取数据
print('主进程的l2:', q.get())
2.3 案例
RIGHT Example:
import requests
import os
from concurrent.futures import ThreadPoolExecutor
from multiprocessing import Process, Queue
def get_all_id():
url = 'https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js'
resp = requests.get(url)
return [x['heroId'] for x in resp.json()['hero']]
def download_img(img_url, path):
resp = requests.get(img_url)
with open(path, 'wb') as f:
f.write(resp.content)
def get_skins(hero_id):
url = f'https://game.gtimg.cn/images/lol/act/img/js/hero/{hero_id}.js'
res = requests.get(url)
data = res.json()
# 获取英雄名称
hero_name = data['hero']['name']
# 创建英雄名称对应的文件夹
if not os.path.exists(f'所有英雄/{hero_name}'):
os.mkdir(f'所有英雄/{hero_name}')
print(f'{hero_name}创建成功')
skins = data['skins']
# 把获取到的英雄皮肤保存到队列中,传递到子进程中
q.put(skins)
def download_one(name, skin_name, img_url):
resp = requests.get(img_url)
with open(f'所有英雄/{name}/{skin_name}.jpg', 'wb') as f:
f.write(resp.content)
print(f'----------{skin_name}-------------')
def download_skin(q: Queue):
# 在子进程中创建线程池,同时下载多个皮肤
pool2 = ThreadPoolExecutor(20)
while True:
data = q.get()
if data == 'end':
break
else:
for x in data:
mainImg = x.get('mainImg')
if mainImg:
hero_name = x['heroName']
skin_name = x['name']
else:
continue
pool2.submit(download_one, hero_name, skin_name, mainImg)
if __name__ == '__main__':
# 1. 在主进程的主线程中获取所有英雄的id
all_id = get_all_id()
# 2. 在主进程中创建线程池获取每个英雄的信息
pool = ThreadPoolExecutor(20)
pool.map(get_skins, all_id)
# 3. 创建子进程完成每个英雄的皮肤下载操作
q = Queue() # 传递皮肤数据
p1 = Process(target=download_skin, args=(q,))
p1.start()
pool.shutdown()
q.put('end')