Python多线程实现 as_completed先返回的任务先处理 在 阿里云 函数式计算 优化的应用

需求:在调用阿里云 函数式计算 时,由于 其函数式计算系统 在分配系统资源时,可能存在 多个任务分配给同一个 服务器(每个服务器2核3G内存),导致 多个相同任务在多线程调用函数式计算时 总有几个 因为资源分配不均返回较慢(包括带宽问题);并且 在多线程 调用函数式计算时,每个线程的函数相同,并且根据业务需求,只要把返回结果list 拼接一下并且满足指定长度即可;

解决方法:使用多线程的 先返回的任务先处理的方法,比如 多线程请求20个函数式计算任务,但是只需要先返回的10个任务即可,剩下的10个任务 忽略;这样能保证 最大程度最大概率的 前10个任务返回总体时间很短,从而 优化接口执行时间;

图示:

 

技术名: python+ThreadPoolExecutor+as_completed

主要技术点:as_completed

实例代码:

from concurrent.futures import ThreadPoolExecutor, as_completed
import time


def get_ali_fun(times):
    time.sleep(times)
    return times


executor = ThreadPoolExecutor()
urls = [3, 2, 4]
all_task = [executor.submit(get_ali_fun, url) for url in urls]

result_list = []
for future in as_completed(all_task):
    data = future.result()
    result_list.append(data)
    print("获取数据,耗时 {}s".format(data))
    if len(result_list) == 2:
        print("先返回的数据已经满足条件 则 剩下的1个线程结果 不再处理。。。")
        break

print("继续处理下面程序")

as_completed()方法是一个生成器,在没有任务完成的时候,会阻塞,在有某个任务完成的时候,会yield这个任务,就能执行for循环下面的语句,然后继续阻塞住,循环到所有的任务结束。从结果也可以看出,先完成的任务会先通知主线程

 

关于阿里云函数式计算相关 知识备注:

1.在调用其服务时,如果需要分析 耗时,需要 考虑 服务内部函数执行时间 和 来回网络带宽时间;

2.截止2020.3.15 问的其技术人员 其 函数计算 在不调用15分钟后 释放资源(以后可能会缩短),如果对时间敏感,可以通过airflow等 使用定时任务方式调用函数计算,不让其释放资源(冷启动至少10s耗费);不要使用 阿里云内置的 预留资源(太贵了。。。)

3.函数式计算系统 在分配资源时 是根据内存进行分配,每个服务器2核3G内存,如果函数 分配内存1G,则有一定 概率 3个函数都在一个服务器上,这样 如果对于并发时间敏感的任务,则有一定的概率 耗时增加;最极端优化方式:一个函数分配3G内存,则保证一个任务1个服务器;缺点是 太费钱。。。

4.如果对时间敏感,需要 考虑 增加带宽(减少带宽导致时间过长的概率,然而时间操作 仍有很大概率),或者 配置 云服务器 和 函数式计算同一个VPC(让其在同一个内网中)

5.如果一个借口 调用多个 函数 进行函数式计算,如果没有 函数间的 依赖,可以考虑 进程+协程 等让其同时 运行 从而节约时间

6.函数式计算的 时间优化 计算优化到尽头 如果 还是需要考虑 资金问题,那仍有 很大概率 无法达到100% 预期时间;因为 函数式计算系统的 资源分配 是随机的,可能 相同函数的 多个任务 都在一个服务器上,这样就比 在不同服务器上 耗时增加;所以 随缘吧。。。

 

as_completed参数 过期时间 设置,注意 如果不添加过期时间,并且函数如果没有返回值,则此线程一直存在,可能造成内存逐渐增加的情况

# -*- coding: utf-8 -*-
"""
(C) Guangcai Ren <[email protected]>
All rights reserved
create time '2020/3/30 18:59'

Module usage:

"""
from concurrent.futures._base import as_completed, TimeoutError
from concurrent.futures.thread import ThreadPoolExecutor


def sleep_fun(_time):
    """
    耗时操作
    :param _time:
    :return:
    """
    import time
    time.sleep(_time)
    return _time


pool = ThreadPoolExecutor()
pool_result = [pool.submit(sleep_fun, func_param) for func_param in [1, 1000]]

try:
    # as_completed添加超时时间,如果不添加,则此 线程池一直存在;
    for result in as_completed(pool_result, 2):
        print(result.result())
    print('do other thing!')
except TimeoutError as e:
    # 清除相关资源,参数 默认为False,需要 所有线程都返回数据才清空资源;参数为 True,则直接清空资源
    pool.shutdown(True)
    print('shut down!')

 

相关链接:

https://www.jianshu.com/p/b9b3d66aa0be

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章