Beispiel #1
0
def download_many():
    '''多线程,按线程数 并发(非并行) 下载所有图片'''
    down_path = setup_down_path()
    links = get_links()

    # 创建队列
    queue = Queue()

    # 创建多个线程
    for i in range(64):
        worker = ThreadWorker(queue)
        worker.daemon = True  # 如果工作线程在等待更多的任务时阻塞了,主线程也可以正常退出
        worker.start()  # 启动线程

    # 往队列中投放任务
    for linkno, link in enumerate(links, 1):  # 链接带序号
        logger.info('Queueing No.{} {}'.format(linkno, link))
        queue.put((down_path, linkno, link))

    logger.info('Waiting for all subthread done...')
    # Causes the main thread to wait for the queue to finish processing all the tasks
    queue.join()
    logger.info('All subthread done.')

    return len(links)
Beispiel #2
0
def download_many_3():
    '''多线程,按线程数 并发(非并行) 下载所有图片
    使用concurrent.futures.ThreadPoolExecutor()
    不使用Executor.map(),而使用Executor.submit()和concurrent.futures.as_completed()
    Executor.submit()方法会返回Future,而Executor.map()是使用Future
    '''
    down_path = setup_down_path()
    links = get_links()

    # 固定住保存的路径,不用每次调用下载图片函数时都传同样的down_path参数
    download_one_1_partial = partial(download_one_1, down_path)

    workers = min(64, len(links))  # 保证线程池中的线程不会多于总的下载任务数
    with futures.ThreadPoolExecutor(workers) as executor:
        to_do = []
        # 创建并排定Future
        for linkno, link in enumerate(links, 1):  # 链接带序号
            future = executor.submit(download_one_1_partial, linkno, link)
            to_do.append(future)
            logger.debug('Scheduled for No.{} {}: {}'.format(
                linkno, link, future))

        results = []
        # 获取Future的结果,futures.as_completed(to_do)的参数是Future列表,返回迭代器,
        # 只有当有Future运行结束后,才产出future
        for future in futures.as_completed(
                to_do):  # future变量表示已完成的Future对象,所以后续future.result()绝不会阻塞
            res = future.result()
            results.append(res)
            logger.debug('{} result: {!r}'.format(future, res))

    return len(results)
def download_many_3():
    '''多进程,按进程数 并行 下载所有图片
    使用multiprocessing.Pool.starmap(download_one_1, images),它是Python-3.3添加的
    可以给download_one_1()函数传元组组成的序列,会自动解包元组给函数的多个参数
    由于下载每张图片时的保存目录都相同,可以使用functools.partial()固定住这个参数
    '''
    down_path = setup_down_path()
    links = get_links()

    # 固定住保存的路径,不用每次调用下载图片函数时都传同样的down_path参数
    download_one_1_partial = partial(download_one_1, down_path)

    images = []
    for linkno, link in enumerate(links, 1):
        images.append((linkno, link))  # 每个元组将不包含保存的目录

    with Pool(4) as p:
        p.starmap(download_one_1_partial, images)  # 链接带序号

    logger.info('Waiting for all subprocesses done...')
    # p.close()
    # p.join()
    logger.info('All subprocesses done.')

    return len(links)
def download_many_4():
    '''多进程,按进程数 并行 下载所有图片
    使用concurrent.futures.ProcessPoolExecutor()
    Executor.map()使用Future而不是返回Future,它返回迭代器,
    迭代器的__next__()方法调用各个Future的result()方法,因此我们得到的是各个Future的结果,而非Future本身

    注意Executor.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语句将调用executor.__exit__()方法,而这个方法会调用executor.shutdown(wait=True)方法,它会在所有进程都执行完毕前阻塞主进程
    with futures.ProcessPoolExecutor(
            max_workers=16
    ) as executor:  # 不指定max_workers时,进程池中进程个数默认为os.cpu_count()
        # executor.map()效果类似于内置函数map(),但download_one()函数会在多个进程中并行调用
        # 它的返回值res是一个迭代器<itertools.chain object>,我们后续可以迭代获取各个被调用函数的返回值
        res = executor.map(download_one, images)  # 传一个序列

    return len(list(res))  # 如果有进程抛出异常,异常会在这里抛出,类似于迭代器中隐式调用next()的效果
def download_many():
    '''依序下载所有图片,同步阻塞'''
    down_path = setup_down_path()
    links = get_links()

    for linkno, link in enumerate(links, 1):
        image = {
            'path': down_path,
            'linkno': linkno,  # 图片序号,方便日志输出时,正在下载哪一张
            'link': link
        }
        download_one(image)

    return len(links)
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)
def download_many_5():
    '''多进程,按进程数 并行 下载所有图片
    使用concurrent.futures.ProcessPoolExecutor()
    Executor.map()中的调用函数如果要接受多个参数,可以给Executor.map()传多个序列
    参考:https://yuanjiang.space/threadpoolexecutor-map-method-with-multiple-parameters
    '''
    down_path = setup_down_path()
    links = get_links()

    # 固定住保存的路径,不用每次调用下载图片函数时都传同样的down_path参数
    download_one_1_partial = partial(download_one_1, down_path)

    # 创建包含所有linkno的序列
    linknos = [i for i in range(len(links))]

    with futures.ProcessPoolExecutor(max_workers=16) as executor:
        res = executor.map(download_one_1_partial, linknos,
                           links)  # 给Executor.map()传多个序列

    return len(list(res))
Beispiel #8
0
def download_many_2():
    '''多线程,按线程数 并发(非并行) 下载所有图片
    使用concurrent.futures.ThreadPoolExecutor()
    Executor.map()中的调用函数如果要接受多个参数,可以给Executor.map()传多个序列
    参考:https://yuanjiang.space/threadpoolexecutor-map-method-with-multiple-parameters
    '''
    down_path = setup_down_path()
    links = get_links()

    # 固定住保存的路径,不用每次调用下载图片函数时都传同样的down_path参数
    download_one_1_partial = partial(download_one_1, down_path)

    # 创建包含所有linkno的序列
    linknos = [i for i in range(len(links))]

    workers = min(64, len(links))  # 保证线程池中的线程不会多于总的下载任务数
    with futures.ThreadPoolExecutor(workers) as executor:
        res = executor.map(download_one_1_partial, linknos,
                           links)  # 给Executor.map()传多个序列

    return len(list(res))
def download_many_2():
    '''多进程,按进程数 并行 下载所有图片
    使用multiprocessing.Pool.starmap(download_one_1, images),它是Python-3.3添加的
    可以给download_one_1()函数传元组组成的序列,会自动解包元组给函数的多个参数
    '''
    down_path = setup_down_path()
    links = get_links()

    images = []
    for linkno, link in enumerate(links, 1):
        images.append((down_path, linkno, link))

    with Pool(4) as p:
        p.starmap(download_one_1, images)  # 链接带序号

    logger.info('Waiting for all subprocesses done...')
    # p.close()
    # p.join()
    logger.info('All subprocesses done.')

    return len(links)
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...')
    # p.close()  # 使用with语句和Pool.map()后,会自动调用Pool.close()和Pool.join()
    # p.join()
    logger.info('All subprocesses done.')

    return len(links)
Beispiel #11
0
def download_many_1():
    '''多线程,按线程数 并发(非并行) 下载所有图片
    使用concurrent.futures.ThreadPoolExecutor()
    Executor.map()使用Future而不是返回Future,它返回迭代器,
    迭代器的__next__()方法调用各个Future的result()方法,因此我们得到的是各个Future的结果,而非Future本身

    注意Executor.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)

    workers = min(64, len(links))  # 保证线程池中的线程不会多于总的下载任务数
    # with语句将调用executor.__exit__()方法,而这个方法会调用executor.shutdown(wait=True)方法,它会在所有进程都执行完毕前阻塞主进程
    with futures.ThreadPoolExecutor(workers) as executor:
        # executor.map()效果类似于内置函数map(),但download_one()函数会在多个线程中并发调用
        # 它的返回值res是一个迭代器<itertools.chain object>,我们后续可以迭代获取各个被调用函数的返回值
        res = executor.map(download_one, images)  # 传一个序列

    return len(list(res))  # 如果有进程抛出异常,异常会在这里抛出,类似于迭代器中隐式调用next()的效果