Python 多线程 中 concurrent.futures 模块使用说明

2023-12-19 19:18

本文主要是介绍Python 多线程 中 concurrent.futures 模块使用说明,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Python 中 concurrent.futures 模块使用说明
转载请注明出处:https://blog.csdn.net/jpch89/article/details/87643972

文章目录
Python 中 concurrent.futures 模块使用说明
0. 参考资料
1. 概述
2. Executor Object 执行器对象
3. ThreadPoolExecutor 线程池执行器
4. ThreadPoolExecutor 例子
5. ProcessPoolExecutor 进程池执行器
6. ProcessPoolExecutor 例子
7. Future 对象
8. 模块函数
9. 待完善
0. 参考资料
concurrent.futures — Launching parallel tasks
concurrent.futures — Manage Pools of Concurrent Tasks
1. 概述
concurrent.futures 是 3.2 中引入的新模块,它为异步执行可调用对象提供了高层接口。
可以使用 ThreadPoolExecutor 来进行多线程编程,ProcessPoolExecutor 进行多进程编程,两者实现了同样的接口,这些接口由抽象类 Executor 定义。
这个模块提供了两大类型,一个是执行器类 Executor,另一个是 Future 类。
执行器用来管理工作池,future 用来管理工作计算出来的结果,通常不用直接操作 future 对象,因为有丰富的 API。

2. Executor Object 执行器对象
concurrent.futures.Executor 类
这个抽象类提供了一系列方法,可以用于异步执行调用。
它不能直接使用,只能通过子类化出来的具体类来使用。

它定义的方法有:
submit(fn, *args, **kwargs)
安排可调用对象 fn 以 fn(*args, **kwargs) 的形式执行,并返回 Future 对象来表示它的执行。

with ThreadPoolExecutor(max_workers=1) as executor:
    future = executor.submit(pow, 323, 1235)
    print(future.result())
1
2
3
map(func, *iterables, timeout=None, chunksize=1)
类似内置函数 map(func, *iterables),但是有两点不同:

立即获取 iterables 而不会惰性获取;
异步执行 func,并支持多次并发调用。
它返回一个迭代器。
从调用 Executor.map() 开始的 timeout 秒之后,如果在迭代器上调用了 __next__() 并且无可用结果的话,迭代器会抛出 concurrent.futures.TimeoutError 异常。
timeout 秒数可以是浮点数或者整数,如果设置为 None 或者不指定,则不限制等待时间。

如果 func 调用抛出了异常,那么该异常会在从迭代器获取值的时候抛出。

当使用 ProcessPoolExecutor 的时候,这个方法会把 iterables 划分成多个块,作为独立的任务提交到进程池。这些块的近似大小可以通过给 chunksize 指定一个正整数。对于很长的 iterables,使用较大的 chunksize 而不是采用默认值 1,可以显著提高性能。对于 ThreadPoolExecutor,chunksize 不起作用。

chunksize 是 3.5 加入的新参数。

注意:不管并发任务的执行次序如何,map 总是基于输入顺序来返回值。map 返回的迭代器,在主程序迭代的时候,会等待每一项的响应。

shutdown(wait=True)
告诉执行器 executor 在当前所有等待的 future 对象运行完毕后,应该释放执行器用到的所有资源。
在 shutdown 之后再调用 Executor.submit() 和 Executor.map() 会报运行时错误 RuntimeError。
如果 wait 为 True,那么这个方法会在所有等待的 future 都执行完毕,并且属于执行器 executor 的资源都释放完之后才会返回。
如果 wait 为 False,本方法会立即返回。属于执行器的资源会在所有等待的 future 执行完毕之后释放。
不管 wait 取值如何,整个 Python 程序在等待的 future 执行完毕之前不会退出。
你可以通过 with 语句来避免显式调用本方法。with 语句会用 wait=True 的默认参数调用 Executor.shutdown() 方法。

import shutil
with ThreadPoolExecutor(max_workers=4) as e:
    e.submit(shutil.copy, 'src1.txt', 'dest1.txt')
    e.submit(shutil.copy, 'src2.txt', 'dest2.txt')
    e.submit(shutil.copy, 'src3.txt', 'dest3.txt')
    e.submit(shutil.copy, 'src4.txt', 'dest4.txt')
1
2
3
4
5
6
执行器类 Executor 实现了上下文协议,可以用做上下文管理器。它能并发执行任务,等待它们全部完成。当上下文管理器退出时,自动调用 shutdown() 方法。

3. ThreadPoolExecutor 线程池执行器
ThreadPoolExecutor 线程池执行器是 Executor 执行器的子类,通过线程池来执行异步调用。它管理一组工作线程,当工作线程有富余的时候,给它们传递任务。
当属于一个 Future 对象的可调用对象等待另一个 Future 的返回时,会发生死锁 deadlock。
举个例子:

import time
def wait_on_b():
    time.sleep(5)
    print(b.result())  # b will never complete because it is waiting on a.
    return 5

def wait_on_a():
    time.sleep(5)
    print(a.result())  # a will never complete because it is waiting on b.
    return 6


executor = ThreadPoolExecutor(max_workers=2)
a = executor.submit(wait_on_b)
b = executor.submit(wait_on_a)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
再举一例:

def wait_on_future():
    f = executor.submit(pow, 5, 2)
    # This will never complete because there is only one worker thread and
    # it is executing this function.
    print(f.result())

executor = ThreadPoolExecutor(max_workers=1)
executor.submit(wait_on_future)
1
2
3
4
5
6
7
8
concurrent.futures.ThreadPoolExecutor(max_workers=None, thread_name_prefix='', initializer=None, initargs=())
这个 Executor 子类最多用 max_workers 个线程来异步执行调用。

initializer 是一个可选的可调用对象,会在每个 worker 线程启动之前调用。
initargs 是传递给 initializer 的参数元组。
如果 initializer 抛出了异常,那么当前所有等待的任务都会抛出 BrokenThreadPool 异常,继续提交 submit 任务也会抛出此异常。

3.5 的变化:如果 max_worker 没有指定或者为 None,则默认为本机处理器数量乘以 5。

3.6 新特性:添加了 thread_name_prefix 参数,可以控制由线程池创建的工作线程名称,便于调试。

3.7 的变化:添加了 initializer 和 initargs 参数。

4. ThreadPoolExecutor 例子
import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
    with urllib.request.urlopen(url, timeout=timeout) as conn:
        return conn.read()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
5. ProcessPoolExecutor 进程池执行器
ProcessPoolExecutor 进程池执行器类是 Executor 执行器类的子类,使用进程池来异步执行调用。
ProcessPoolExecutor 使用了 multiprocessing 模块,这允许它可以规避 Global Interpreter Lock,但是也意味着只能执行和返回可序列化的(picklable)对象。

__main__ 模块必须被 worker 子进程导入,这意味着 ProcessPoolExecutor 在交互解释器中无法工作。

在已经被提交到 ProcessPoolExecutor 中的可调用对象内使用 Executor 或者 Future 方法会导致死锁。

concurrent.futures.ProcessPoolExecutor(max_workers=None, mp_context=None, initializer=None, initargs=())
这个 Executor 子类最多用 max_workers 个进程来异步执行调用。
如果不指定 max_workers 或者为 None,它默认为本机的处理器数量。
如果 max_workers 小于等于 0,会抛出 ValueError 异常。
mp_context 是多进程上下文(multiprocessing context)或者 None,它会被用来启动 workers。如果不指定 mp_context 或者为 None,会使用默认的多进程上下文环境。

initializer 是一个可选的可调用对象,会在每个 worker 进程启动之前调用。
initargs 是传递给 initializer 的参数元组。
如果 initializer 抛出了异常,那么当前所有等待的任务都会抛出 BrokenProcessPool 异常,继续提交 submit 任务也会抛出此异常。

3.3 版本的变化:任意一个工作进程突然中止时,会抛出 BrokenProcessPool 异常。之前版本中,行为是未定义的,而且对于执行器或者它的 future 对象的操作通常会无响应或者死锁。

3.7 版本的变化:加入了 mp_context 参数,允许用户控制由进程池创建的工作进程的 start_method 方法。该版本还加入了 initializer 和 initargs 参数。

6. ProcessPoolExecutor 例子
import concurrent.futures
import math

PRIMES = [
    112272535095293,
    112582705942171,
    112272535095293,
    115280095190773,
    115797848077099,
    1099726899285419]

def is_prime(n):
    if n % 2 == 0:
        return False

    sqrt_n = int(math.floor(math.sqrt(n)))
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True

def main():
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
            print('%d is prime: %s' % (number, prime))

if __name__ == '__main__':
    main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
7. Future 对象
Future 类封装了可调用对象的异步执行。
Future 实例通过 Executor.submit() 创建。

concurrent.futures.Future
封装了可调用对象的异步执行。
Future 实例通过 Executor.submit() 创建,除非用于测试,不应该直接手动创建。

cancel() 尝试取消调用,如果该调用正在执行中,无法取消,本方法返回 False,其他情况下调用会被取消,并返回 True。
理解:当一个 Future 实例已经被提交但是还没开始执行时,可以通过调用该实例的 cancel() 方法取消。

cancelled() 如果调用已经被成功取消,返回 True。
running() 如果调用正在执行,无法被取消,则返回 True。
done() 如果调用成功被取消或者已经执行完毕,返回 True。
result(timeout=None) 返回调用的返回值。如果调用还没有完成,则最多等待 timeout 秒。如果 timeout 秒之后还没有完成,抛出 concurrent.futures.TimeoutError。timeout 可以为整数或者浮点数。如果不指定或者为 None,则不限制等待时间。如果 future 在完成之前被取消了,会抛出 CancelledError 异常。
如果调用抛出异常,这个方法会抛出同样的异常。
理解:result() 阻塞直到任务完成,或者被取消。如果需要按照顺序访问结果,使用执行器的 map 方法,如果不需要按照顺序访问结果,可以使用模块函数 as_completed()。

exception(timeout=None)
返回被调用抛出的异常。如果调用还没有执行完毕,则最多等待 timeout 秒。如果 timeout 秒之后还没有完成,抛出 concurrent.futures.TimeoutError。timeout 可以为整数或者浮点数。如果不指定或者为 None,则不限制等待时间。
如果 future 在完成之前被取消了,会抛出 CancelledError 异常。
如果调用完成并且没有抛出异常,返回 None。
add_done_callback(fn)
为 future 附加可调用对象 fn。当 future 运行完毕或者被取消时,它会被用作 fn 的唯一参数,并调用 fn。
可调用对象按照添加顺序依次调用,并且总是在添加时所处进程的一个线程内调用它。如果该可调用对象抛出了属于 Exception 子类的异常,它会被记录并忽略。如果它抛出了属于 BaseException 子类的异常,该行为未定义。
如果 future 已经完成或者已经取消,fn 会被立即调用。
理解:这个方法不用显式地等待结果返回,可以事先指定当 Future 完成之后应当执行的调用。fn 是一个单参数的可调用对象,使用传入的 Future 对象之前最好先检查它的状态,因为不管是正常结束、抛出异常还是被取消,Future 对象都会被认为执行完毕。

8. 模块函数
concurrent.futures.wait(fs, timeout=None, return_when=ALL_COMPLETED)
等待 Future 实例完成,这些实例可能由多个不同的执行器实例创建,通过 fs 指定这些 Future 实例。返回具名元组,该元组有两个元素,每个元素都是一个集合。第一个元素名叫 done,该集合包括已完成的 futures;第二个元素名叫 not_done,该集合包括未完成的 futures。
timeout 用来控制返回之前等待的最大秒数,可以是整数或者浮点数。如果不指定或为 None,不限制等待时间。
return_when 指明函数何时应该返回。它必须是下列常量之一:

FIRST_COMPLETED:函数在任意一个 future 完成或者被取消时返回。
FIRST_EXCEPTION:函数在任意一个 future 因为异常而结束时返回。如果没有 future 抛出异常,它等价于 ALL_COMPLETED。
ALL_COMPLETED:当所有 future 完成或者被取消时函数才会返回。
concurrent.futures.as_completed(fs, timeout=None)
当通过 fs 指定的 Future 实例全部执行完毕或者被取消后,返回这些 Future 实例组成的迭代器。fs 中的 Future 实例可以被不同的执行器创建。任何在 as_completed() 调用之前就已经完成的 Future 实例会被最先生成。

查看源码发现,实际上这是一个用到了 yield from 的生成器函数,所以调用返回一个生成器。

如果从 as_completed() 调用开始,经过 timeout 秒之后,对返回的迭代器调用 __next__() 时结果仍不可用,则会抛出 concurrent.futures.TimeoutError 异常。timeout 可以是整数或者浮点数,如果 timeout 没有指定或者为 None,则不限制等待时间。

9. 待完善
Future 内部方法:用于单元测试和实现自定义执行器的方法。
set_running_or_notify_cancel()
set_result(result)
set_exception(exception)
相关异常类
 

这篇关于Python 多线程 中 concurrent.futures 模块使用说明的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/513386

相关文章

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

python常见环境管理工具超全解析

《python常见环境管理工具超全解析》在Python开发中,管理多个项目及其依赖项通常是一个挑战,下面:本文主要介绍python常见环境管理工具的相关资料,文中通过代码介绍的非常详细,需要的朋友... 目录1. conda2. pip3. uvuv 工具自动创建和管理环境的特点4. setup.py5.

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

Python UV安装、升级、卸载详细步骤记录

《PythonUV安装、升级、卸载详细步骤记录》:本文主要介绍PythonUV安装、升级、卸载的详细步骤,uv是Astral推出的下一代Python包与项目管理器,主打单一可执行文件、极致性能... 目录安装检查升级设置自动补全卸载UV 命令总结 官方文档详见:https://docs.astral.sh/

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Before和BeforeClass的区别及说明

《Before和BeforeClass的区别及说明》:本文主要介绍Before和BeforeClass的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Before和BeforeClass的区别一个简单的例子当运行这个测试类时总结Before和Befor

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Python虚拟环境与Conda使用指南分享

《Python虚拟环境与Conda使用指南分享》:本文主要介绍Python虚拟环境与Conda使用指南,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、python 虚拟环境概述1.1 什么是虚拟环境1.2 为什么需要虚拟环境二、Python 内置的虚拟环境工具