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

 

 

 

 

 

 

 

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