使用线程池并发执行子进程以提高效率
本文旨在介绍如何使用Python 的subprocess 模块并发执行多个子进程,并通过线程池来显着提高程序的执行效率。我们将分析常见的使用subprocess.Popen 和.communicate() 方法的场景,并提供使用ThreadPool 并发等待子进程完成的示例代码。
在使用subprocess 模块执行多个子进程时,一个常见的误解是Popen 函数会阻塞程序的执行。实际上,Popen 函数是非阻塞的,它会立即返回一个Popen 对象,允许程序继续执行。然而,Popen 对象的communicate() 方法是阻塞的,它会等待子进程执行完毕并返回其输出。如果在循环中依次调用communicate() 方法,实际上会导致子进程按顺序执行,从而降低程序的效率。
为了解决这个问题,可以使用线程池来并发等待子进程完成。线程池可以创建多个线程,每个线程负责等待一个子进程完成。这样,多个子进程可以同时运行,从而提高程序的执行效率。
下面是一个使用线程池并发等待子进程完成的示例代码:
import subprocess import logging from multiprocessing.pool import ThreadPool logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') log = logging.getLogger(__name__) def runShowCommands(cmdTable) -> dict: """return a dictionary of captured output from commands defined in cmdTable.""" procOutput = {} # dict to store the output text from show commands procHandles = {} for cmd, command in cmdTable.items(): try: log.debug(f"running subprocess {cmd} -- {command}") procHandles[cmd] = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) except Exception as e: log.error(f"Error launching subprocess {cmd}: {e}") continue def handle_proc_stdout(handle): try: proc = procHandles[handle] stdout, stderr = proc.communicate(timeout=180) procOutput[handle] = stdout.decode("utf-8") # turn stdout portion into text log.debug(f"subprocess returned {handle}") if stderr: log.error(f"subprocess {handle} returned stderr: {stderr.decode('utf-8')}") except subprocess.TimeoutExpired: log.error(f"subprocess {handle} timed out") proc.kill() # Terminate the process except Exception as e: log.error(f"Error handling subprocess {handle}: {e}") threadpool = ThreadPool() threadpool.map(handle_proc_stdout, procHandles.keys()) threadpool.close() threadpool.join() return procOutput if __name__ == '__main__': cmdTable = { 'himom': "echo hi there momma", 'goodbye': "echo goodbye", 'date': "date", 'sleep': "sleep 2 && echo slept" } output = runShowCommands(cmdTable) for cmd, out in output.items(): print(f"Output from {cmd}:\n{out}")
代码解释:
-
runShowCommands(cmdTable) 函数:
- 接受一个字典cmdTable,其中键是命令的名称,值是要执行的命令字符串。
- 创建一个空字典procOutput 来存储每个命令的输出。
- 创建一个空字典procHandles 来存储每个Popen 对象。
- 循环遍历cmdTable 中的每个命令:
- 使用subprocess.Popen 启动子进程,并将stdout 和stderr 重定向到管道。 shell=True 允许直接执行字符串命令,但要注意安全性。
- 将Popen 对象存储在procHandles 字典中,键是命令名称。
- 定义一个内部函数handle_proc_stdout(handle):
- 此函数负责处理单个子进程的输出。
- 使用procHandles[handle].communicate(timeout=180) 获取子进程的输出,并设置超时时间为180 秒。
- 将输出解码为UTF-8 字符串,并将其存储在procOutput 字典中。
- 记录子进程返回的消息。
- 处理TimeoutExpired 异常,如果子进程超时,则记录错误并终止该进程。
- 处理其他异常,如果发生任何其他错误,则记录错误消息。
- 创建一个ThreadPool 对象。
- 使用threadpool.map(handle_proc_stdout, procHandles.keys()) 将handle_proc_stdout 函数应用于procHandles 字典中的每个键(命令名称)。 这会在线程池中并行执行handle_proc_stdout 函数。
- 调用threadpool.close() 以防止向线程池提交更多任务。
- 调用threadpool.join() 以等待所有线程完成。
- 返回procOutput 字典。
-
if __name__ == '__main__': 块:
- 创建一个示例cmdTable 字典。
- 调用runShowCommands(cmdTable) 执行命令并获取输出。
- 循环遍历output 字典,并打印每个命令的输出。
注意事项:
- 超时处理: communicate(timeout=180) 设置了超时时间,防止子进程无限期运行。如果子进程在指定时间内未完成,将引发TimeoutExpired 异常,并且该进程将被终止。
- 错误处理:代码包含try...except 块,用于捕获可能发生的异常,例如子进程启动失败或超时。
- 线程安全:确保在多线程环境中访问和修改共享资源(例如procOutput 字典)是线程安全的。 在此示例中,由于每个线程都写入不同的键,因此字典的写入操作是线程安全的。
- 资源限制:创建过多的线程可能会消耗大量系统资源。 线程池的大小应根据系统资源和任务的性质进行调整。
- 安全性:使用shell=True 执行命令时,需要注意命令注入的风险。 确保命令字符串来自可信来源,或者对输入进行适当的转义。
- 日志记录:使用logging 模块记录程序的运行状态,方便调试和排错。
总结:
通过使用线程池,可以并发执行多个子进程,从而显着提高程序的执行效率。 在处理大量并发任务时,线程池是一种非常有用的技术。 请记住考虑超时处理、错误处理、线程安全性和资源限制等因素,以确保程序的正确性和稳定性。
以上是使用线程池并发执行子进程以提高效率的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undress AI Tool
免费脱衣服图片

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Stock Market GPT
人工智能驱动投资研究,做出更明智的决策

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

运行pipinstall-rrequirements.txt可安装依赖包,建议先创建并激活虚拟环境以避免冲突,确保文件路径正确且pip已更新,必要时使用--no-deps或--user等选项调整安装行为。

本教程详细介绍了如何将PEFT LoRA适配器与基础模型高效合并,生成一个完全独立的模型。文章指出直接使用transformers.AutoModel加载适配器并手动合并权重是错误的,并提供了使用peft库中merge_and_unload方法的正确流程。此外,教程还强调了处理分词器的重要性,并讨论了PEFT版本兼容性问题及解决方案。

Pytest是Python中简单强大的测试工具,安装后按命名规则自动发现测试文件。编写以test_开头的函数进行断言测试,使用@pytest.fixture创建可复用的测试数据,通过pytest.raises验证异常,支持运行指定测试和多种命令行选项,提升测试效率。

本文旨在探讨Python及NumPy中浮点数计算精度不足的常见问题,解释其根源在于标准64位浮点数的表示限制。针对需要更高精度的计算场景,文章将详细介绍并对比mpmath、SymPy和gmpy等高精度数学库的使用方法、特点及适用场景,帮助读者选择合适的工具来解决复杂的精度需求。

theargparsemodulestherecommondedwaywaytohandlecommand-lineargumentsInpython,提供式刺激,typeValidation,helpmessages anderrornhandling; useSudys.argvforsimplecasesRequeRequeRingminimalSetup。

PyPDF2、pdfplumber和FPDF是Python处理PDF的核心库。使用PyPDF2可进行文本提取、合并、拆分及加密,如通过PdfReader读取页面并调用extract_text()获取内容;pdfplumber更适合保留布局的文本提取和表格识别,支持extract_tables()精准抓取表格数据;FPDF(推荐fpdf2)用于生成PDF,通过add_page()、set_font()和cell()构建文档并输出。合并PDF时,PdfWriter的append()方法可集成多个文件

获取当前时间在Python中可通过datetime模块实现,1.使用datetime.now()获取本地当前时间,2.用strftime("%Y-%m-%d%H:%M:%S")格式化输出年月日时分秒,3.通过datetime.now().time()获取仅时间部分,4.推荐使用datetime.now(timezone.utc)获取UTC时间,避免使用已弃用的utcnow(),日常操作以datetime.now()结合格式化字符串即可满足需求。

本教程详细演示了如何利用Python的Pandas库高效地从多个文本文件中提取、关联并整合特定数据。通过将文件数据加载为DataFrame,并使用merge操作进行基于IP地址和MAC地址的内连接,最终实现从不同来源的文件中精确匹配并输出IP、MAC地址及对应端口的关联信息。
