Python 3 爬虫|第4章:多进程并发下载

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

Python 3 爬虫-min.png

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

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

1. 使用multiprocessing

承接 Python 3 爬虫|第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(
                                
                            
未经允许不得转载: LIFE & SHARE - 王颜公子 » Python 3 爬虫|第4章:多进程并发下载

分享

作者

作者头像

Madman

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

0 条评论

暂时还没有评论.

专题系列