Python3爬虫系列04 (实验) - 多进程并发下载

  • 原创
  • Madman
  • /
  • /
  • 0
  • 2216 次阅读

spider 04-min.jpg

Synopsis: I/O密集型适合使用多线程,CPU密集型适合使用多进程。当然,我们还是可以利用多进程将下载速度有一定的提升。Python3中可以使用multiprocessing模块和concurrent.futures.ProcessPoolExecutor进程池模块,实现多进程

代码已上传到 https://github.com/wangy8961/python3-concurrency ,欢迎star

1. 使用multiprocessing

承接 3 实验 - 同步阻塞下载 ,我们试一下用多进程的效果会怎样?

1.1 Pool.apply_async()

创建processpool.py模块:

import time
from multiprocessing import Pool
from common import setup_down_path, get_links, download_one
from logger import logger


def download_many():
    '''多进程,按进程数 并行 下载所有图片
    使用multiprocessing.Pool.apply_async()
    '''
    down_path = setup_down_path()
    links = get_links()

    p = Pool(4)  # 指定进程池中的进程数
    for linkno, link in enumerate(links, 1):
        image = {
            'path': down_path,
            'linkno': linkno,
            'link': link
        }
        p.apply_async(download_one, args=(image,))

    logger.info('Waiting for all subprocesses done...')
    p.close()  # 关闭进程池
    p.join()  # 主进程等待进程池中的所有子进程结束
    logger.info('All subprocesses done.')

    return len(links)


if __name__ == '__main__':
    t0 = time.time()
    count = download_many()
    msg = '{} flags downloaded in {} seconds.'
    logger.info(msg.format(count, time.time() - t0))

我的Win10主机有4颗CPU核心:

In [1]: import os

In [2]: os.cpu_count()
Out[2]: 4

多进程的测试结果:

进程线 用时 备注
4 13.86秒 p = Pool(4)
8 8.88秒 p = Pool(8)
16 5.90秒 p = Pool(16)
32 7.48秒 p = Pool(32)
64 12.48秒 p = Pool(64)
128 23.08秒 p = Pool(128)

第一篇依序下载平均用时50秒,理论上4个进程应该是12.5秒,而测试的结果是13.86秒,原因是,创建进程需要时间,比如使用128个进程测试,你会发现前面要等待很长一段时间才会有进程真正开始下载。另外,当进程数大于CPU核心数时,必然会发生进程间切换(其实就算只有4个进程也会有进程切换,因为系统开机后就运行了很多进程,也就是说你开4个进程来下载,同一时刻并不是4个下载的进程都在4核CPU上运行),开销非常大,所以你会发现并不是启动越多进程效率越高

一般服务器资源是有限的,操作系统在稳定运行的前提下,可以同时处理的进程数在数十个到数百个规模。如果进程数量规模更大,系统运行将不稳定,而且可用内存资源往往也会不足。

1.2 Pool.map()

Pool.map()类似于内置的map()函数,它将可迭代的序列映射到调用的函数上,注意: 调用的函数只能接受一个参数

def download_many_1():
    '''多进程,按进程数 并行 下载所有图片
    使用multiprocessing.Pool.map(download_one, images)
    注意Pool.map()限制了download_one()只能接受一个参数,所以images是字典构成的列表
    '''
    down_path = setup_down_path()
    links = get_links()

    images = []
    for linkno, link in enumerate(links, 1):
        image = {
            'path': down_path,
            'linkno': linkno,
            'link': link
        }
        images.append(image)

    with Pool(4) as p:
        p.map(download_one, images)  # 将images序列依次映射给download_one()函数

    logger.info('Waiting for all subprocesses done...'
                                
                            
未经允许不得转载: LIFE & SHARE - 王颜公子 » Python3爬虫系列04 (实验) - 多进程并发下载

分享

作者

作者头像

Madman

如需 Linux / Python 相关问题付费解答,请按如下方式联系我

0 条评论

暂时还没有评论.