【发布时间】:2018-10-06 21:54:06
【问题描述】:
我正在使用 python 3.6.6 和 requests 和 bs4 包来下载和解析一些内容,现在我正在下载一些更大的文件 >1gb 并且只使用一个连接它相当慢所以我想加快它同时进行多个下载。
重要的代码片段:
def download(dir, link, name):
r = requests.get(url, stream=True)
with open(f'{path}/{filename}', 'wb') as f:
for chunk in r.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
files = [{'link':'http://...','filename':'somename.7z'}]
download_dir= '~/Downloads'
for file in files:
#do some things to check if file['link'] is valid and that the file dosen't already exist
download(download_dir, file['link'], file['filename'])
我想做的是并行运行循环中的内容,确切地说是让循环中的内容同时运行 4 次。
我第一次尝试这样做是像这样使用multiprocessing.Pool.map:
def download(dir, link, name):
r = requests.get(url, stream=True)
with open(f'{path}/{filename}', 'wb') as f:
for chunk in r.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
files = [{'link':'http://...','filename':'somename.7z'}]
download_dir= '~/Downloads'
datas = [{'file':f, 'dir':download_dir} for f in files]
worker(data)
file = data['file']
download_dir = data['dir']
#do some things to check if file['link'] is valid and that the file dosen't already exist
download(download_dir, file['link'], file['filename'])
pool = multiprocessing.Pool(4)
pool.map(worker, datas)
不幸的是,这不起作用,同时开始了超过 4 个下载,我假设它使用了 4 个线程,但是每次一个线程达到网络限制并且没有一个旧的更进一步,它只是启动了另一个 worker 实例.
为了强迫我的程序做我想做的事,我尝试了这种 hacky 方式:
def download(dir, link, name):
r = requests.get(url, stream=True)
with open(f'{path}/{filename}', 'wb') as f:
for chunk in r.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
files = [{'link':'http://...','filename':'somename.7z'}]
download_dir= '~/Downloads'
worker(file, download_dir)
#do some things to check if file['link'] is valid and that the file dosen't already exist
download(download_dir, file['link'], file['filename'])
index = 0
while index < len(files):
pool = multiprocessing.Pool(4)
for _ in range(4):
if index < len(files): #check exists cause I'm incrementing index in the inner for loop
pool.apply_async(worker, (files[index], download_dir,))
index += 1
pool.close()
pool.join()
但是pool.close() 并没有等待所有提交的任务完成,而是中止了下载,并且显然也不允许提交到池的任务在搁置后恢复。
这样做的正确方法是什么?
【问题讨论】:
-
你能解释一下“线程达到网络限制”是什么意思吗?是否引发了异常,或者进程只是阻塞等待 IO 完成?有错误信息吗?您是如何认识到这一点的?网络限制是多少(连接数、防火墙、带宽等)?
-
我很抱歉不清楚这一点,我认为它会阻塞 IO,因为代码运行在谷歌云中的 vm 上,下载速度至少为 500Mbit/s 但是当我研究如何很多数据是单线程传输的,它大约是 10Mbit/s。所以我假设正在发生的是
for chunk in r.iter_content(chunk_size=1024)必须等待获得 1024 个字节,并且在那个时候线程池开始另一个并行执行。
标签: python python-multiprocessing