python-concurrent

发布时间 2023-03-26 17:18:23作者: 贝壳里的星海

python-concurrent

概述

__all__ = (
    'FIRST_COMPLETED',
    'FIRST_EXCEPTION',
    'ALL_COMPLETED',
    'CancelledError',
    'TimeoutError',
    'BrokenExecutor',
    'Future',
    'Executor',
    'wait',
    'as_completed',
    'ProcessPoolExecutor',
    'ThreadPoolExecutor',
)

Executor

Executor 是一个抽象类,不应该直接使用此类,而是使用它提供的两个子类:ThreadPoolExecutor 和 ProcessPoolExecutor

函数方法 含义
submit(fn,*args,**kwargs)->Future 提交执行的函数及其参数,返回Future类的实例
shutdown(wait=True) 所有挂起的 Future 对象执行完成时,释放正在使用的任何资源
map(func,*iterables,)  类似于map函数,提供映射计算。 如果函数调用引发异常,当从迭代器检索其值时将引发异常,并且不会继续执行;

Future

Futute 类封装可调用对象的异步执行,应由Executor.submit() 方法创建

方法 含义
done() 调用被成功的取消或者执行完成,返回True
cancelled() 调用被成功的取消,返回True
running() 正在运行且不能被取消,返回True
cancel() 尝试取消调用。如果已经执行且不能取消返回False,否则返回True
result(timeout=None) 获取返回的结果,timeout默认为None,表示永久阻塞直到返回结果。
exception(timeout=None) 获取返回的异常
add_done_callback(fn) future执行完成后,添加回调

concurrent.futures

异步并行任务编辑模块,提供一个高级的异步可执行的便利接口。

  • concurrent.functures库统一了线程池和进程池

  • ThreadPoolExecutor 异步调用的线程池的 Executor

  • ProcessPoolExecutor 异步调用的进程池的 Executor

ThreadPoolExecutor线程池与ProcessPoolExecutor进程池对象

ThreadPoolExecutor

线程构造

ThreadPoolExecutor(max_workers=None, thread_name_prefix=’’, initializer=None, initargs=())->Future

max_workers 最大线程个数
mp_context 允许用户控制池创建的worker进程的start_method
initializer 每个工作进程创建开始时调用的函数,即进程创建后的第一个初始化函数
initargs 初始化 initializer 中的位置参数
from concurrent.futures import ThreadPoolExecutor
import time
import logging
import threading
import sys

FORMAT = "%(asctime)-15s [%(processName)s:%(threadName)s, %(process)d:%(thread)8d] %(message)s"
logging.basicConfig(stream=sys.stdout,level=logging.INFO,format=FORMAT)
logger = logging.getLogger(__name__)

def worker(n):
    logger.info(f'start thread {n}')
    time.sleep(n+1)
    logger.info(f'end thread {n}')
    return n

executor = ThreadPoolExecutor(max_workers=3)

futurelist = []
for i in range(3):
    logger.info(f"开始异步 {i}")
    future = executor.submit(worker, i)     # 异步-非阻塞-直接执行-执行完毕后立马执行下一条命令
    futurelist.append(future)

while True:
    logger.info(f"当前线程情况 {threading.enumerate()}")
    
    
    flag = True
    for i,f in enumerate(futurelist):
        logger.info(f"是否执行结束 {i} {f.done()}")
        flag = flag and f.done()

    if flag:
        executor.shutdown()  # 清理线程池
        logger.info(f"全部执行完毕 {threading.enumerate()}")
        break
    
    time.sleep(1)

for f in futurelist:
    logger.info(f.result())

ProcessPoolExecutor

进程构造

concurrent.futures.ProcessPoolExecutor(max_workers=None, mp_context=None, initializer=None, initargs=())

max_workers 最大进程个数
mp_context 允许用户控制池创建的worker进程的start_method
initializer 每个工作进程创建开始时调用的函数,即进程创建后的第一个初始化函数
initargs  初始化 initializer 中的位置参数
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
from concurrent import futures
import threading,multiprocessing
import logging
import time,sys

FORMAT = "%(asctime)-15s [%(processName)s:%(threadName)s, %(process)d:%(thread)8d] %(message)s"
logging.basicConfig(stream=sys.stdout,level=logging.INFO,format=FORMAT)
logger = logging.getLogger(__name__)


def init():
    logger.info("初始化任务>>>>")

def worker(i):
    logger.info(f"开始工作了 {i}")
    time.sleep(i+1)
    logger.info(f"工作完成 {i}")
    return i

def callback(fs):
    logger.info(f"回调函数执行{fs.result()}")

def callback2(fs):
     logger.info(f"222回调函数执行{fs.result()}")


if __name__ == "__main__":
    executerPro = ProcessPoolExecutor(max_workers=3,  initializer=init)

    time.sleep(0.1)
    # 注意 ProcessPoolExecutor 是懒惰模式,使用 submit 才会创建  
    print(threading.enumerate(),multiprocessing.active_children())

    fs = []
    for i in range(3):
        f = executerPro.submit(worker,i)
        f.add_done_callback(callback)
        f.add_done_callback(callback2)
        fs.append(f)

    print(threading.enumerate())
    print(multiprocessing.active_children())
    

    done,not_done = futures.wait(fs,return_when=futures.ALL_COMPLETED)  # 当所有执行完毕后返回,见如下wait解析
    for i in done:
        print(i.result())
    print("开始打印not_down....")
    for i in not_done:
        print(i.result())

wait 和 as_completed

wait(fs, timeout=None, return_when=ALL_COMPLETED)
  • 遍历future(可能由不同的Executor实例创建),并等待执行完成,如果未设置timeout参数,则代表不限制等待时间,return_when参数则用于设置次函数应该在何时返回,支持的选项如下:
Constant Description
FIRST_COMPLETED 在当有任何future执行完成(包括已取消)时返回结果
FIRST_EXCEPTION 当有任何future执行引发异常时返回结果,若没有任何future引发异常则等同于ALL_COMPLETED
ALL_COMPLETED 当所有future执行完成(包括已取消)时返回结果

按照线程池中线程完成的顺序返回, 而不是线程加载的顺序返回


from concurrent.futures import ThreadPoolExecutor, as_completed
import time
import random

def do_something(i):
    
    rad=random.randint(1, 5)
    print (f'线程{i}, 休眠{rad}秒')
    time.sleep(rad)
    return f'休眠完毕{i},{rad}'

start_time = time.perf_counter()

executor=ThreadPoolExecutor()
results = [executor.submit(do_something, i) for i in range(10)]
print(results)
for f in as_completed(results):
    print (f.result())

end_time = time.perf_counter()-start_time

print (f'总共耗时{round(end_time,2)}秒')

# 非指定顺序返回
休眠完毕2,1
休眠完毕8,1
休眠完毕6,2
休眠完毕1,2
休眠完毕4,3
休眠完毕5,4
休眠完毕9,4
休眠完毕7,5
休眠完毕3,5
休眠完毕0,5
总共耗时5.01秒

映射计算map

map方法则返回迭代器,在出现异常的时候,会全部报错,详细见异常处理

from concurrent.futures import ThreadPoolExecutor
import time

def do_something(seconds):
    print (f'休眠{seconds}秒')
    time.sleep(seconds)
    return '休眠完毕'

start_time = time.perf_counter()

executor=ThreadPoolExecutor()
sec = [5,4,3,2,1]
results = executor.map(do_something, sec)
for result in results:
 print (result)

end_time = time.perf_counter()-start_time

print (f'总共耗时{round(end_time,2)}秒')

支持上下文管理

使用with来避免使用 shutdown(wait=True)

with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    future1 = executor.submit(f, 0)
    future2 = executor.submit(f, 0)
    future3 = executor.submit(f, 20)

异常处理

submit 方法返回的是 Future 对象, map方法则返回迭代器,如果没有调用future对象的result方法,即使执行过程中有异常用户也是不知道的

f = lambda x: 100 // x

with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    future1 = executor.submit(f, 0)
    future2 = executor.submit(f, 0)
    future3 = executor.submit(f, 20)

todos = [future1, future2, future3]
for future in concurrent.futures.as_completed(todos):
    try:
        print(future.result())
    except ZeroDivisionError as e:
        print(e.__repr__())

>>>
ZeroDivisionError('integer division or modulo by zero')
5
ZeroDivisionError('integer division or modulo by zero')
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    # If a func call raises an exception, then that exception will be raised when its value is retrieved from the iterator.
    futures = executor.map(f, [0, 10, 20])

while True:
    try:
        future = futures.__next__()
    except StopIteration:
        break
    except ZeroDivisionError as e:
        print(e.__repr__())
 
>>>
ZeroDivisionError('integer division or modulo by zero')

参考文件