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 <rgc@bvrft.com>
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:
    # 清除相关资源,参数 默认为True,需要 所有线程都返回数据才清空资源;参数为 False,则直接清空资源,继续执行后续程序
    pool.shutdown(False)
    print('shut down!')
print('do something!!!')

相关链接:

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

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
`concurrent.futures.as_completed()`是一个非常有用的函数,它可以迭代返回已完成的`Future`对象。在使用线程池执行一批任务时,可以使用`as_completed()`来获取已完成的任务的结果。 下面是一个示例: ```python import concurrent.futures # 定义一个任务函数 def task(name): print(f"Task {name} starting") # 执行具体任务的代码 print(f"Task {name} done") return f"Task {name} result" # 创建线程池 with concurrent.futures.ThreadPoolExecutor() as executor: # 提交任务到线程池 futures = [executor.submit(task, i) for i in range(5)] # 获取已完成任务的结果 for future in concurrent.futures.as_completed(futures): result = future.result() print(result) ``` 在上面的示例中,我们首定义了一个任务函数`task`,然后使用`ThreadPoolExecutor`创建了一个线程池,并使用`submit`方法提交了5个任务到线程池中。返回的`futures`是一个包含所有任务的`Future`对象列表。 接下来,在使用`as_completed()`函数时,它会返回一个迭代器,可以逐个获取已完成的任务。我们使用`future.result()`方法获取任务的结果,并打印出来。 需要注意的是,`as_completed()`函数返回的是一个迭代器,它会按照任务完成的顺序返回结果。如果一个任务比其他任务耗时更长,那么在迭代结果时,可能会返回其他任务的结果。因此,你可以根据实际需求来处理返回的结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值