当前位置: 首页 > article >正文

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.25 多线程并行:GIL绕过与真正并发

在这里插入图片描述

2.25 多线程并行:GIL绕过与真正并发

目录

2.25 多线程并行:GIL绕过与真正并发
2.25.1 NumPy的GIL释放机制
2.25.2 线程池配置与使用
2.25.3 原子操作与竞态条件
2.25.4 图像批处理案例
2.25.5 竞态条件调试

2.25.1 NumPy的GIL释放机制

2.25.1.1 GIL简介

Python 的全局解释器锁(Global Interpreter Lock,简称 GIL)是一个互斥锁,用于保护对 Python 对象的访问。GIL 确保在多线程环境中,同一时刻只有一个线程在执行字节码。这在很多情况下是一个性能瓶颈,特别是在多核处理器上。

2.25.1.2 为什么 NumPy 可以绕过 GIL

NumPy 是一个用 C 语言编写的高效数值计算库。NumPy 的底层函数是用 C 语言实现的,可以暂时释放 GIL 以进行高效的计算。这使得 NumPy 在多线程环境中能够实现真正的并行计算。

2.25.1.3 释放 GIL 的原理

NumPy 通过在 C 扩展中暂时释放 GIL 来实现多线程并行。具体步骤如下:

  1. 释放 GIL:在 C 扩展函数中,使用 Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS 宏来释放和重新获取 GIL。
  2. 使用多线程库:在释放 GIL 的期间,可以使用多线程库(如 OpenMP)来并行执行计算任务。
  3. 重新获取 GIL:完成计算任务后,重新获取 GIL,以确保 Python 解释器的安全性。

2.25.1.4 代码示例

import numpy as np
import threadingdef compute_mean(array):# 释放 GILnp.core._rational.arithmetic._begin_threads()# 进行计算result = np.mean(array)  # 计算数组的均值# 重新获取 GILnp.core._rational.arithmetic._end_threads()return result# 创建一个大数组
data = np.random.rand(10000000)# 创建多个线程
threads = []
for i in range(4):thread = threading.Thread(target=compute_mean, args=(data,))threads.append(thread)thread.start()# 等待所有线程完成
for thread in threads:thread.join()print("计算完成")

2.25.1.5 优缺点

  • 优点

    • 提高性能:在多核处理器上,释放 GIL 可以显著提高计算性能。
    • 简化开发:NumPy 的高层接口隐藏了多线程的复杂性,使得开发更加简单。
  • 缺点

    • 增加复杂性:在底层释放 GIL 需要对 C 语言有一定的了解。
    • 安全性问题:不当的 GIL 操作可能导致数据不一致和竞态条件。

2.25.2 线程池配置与使用

2.25.2.1 线程池简介

线程池(Thread Pool)是一种多线程处理形式,处理过程中将任务添加到队列中,然后在创建线程后自动启动这些任务。线程池可以有效管理和复用线程资源,减少线程创建和销毁的开销。

2.25.2.2 使用 concurrent.futures 配置线程池

concurrent.futures 是 Python 标准库中的一个高级线程池接口,使用非常方便。

2.25.2.3 代码示例

import numpy as np
from concurrent.futures import ThreadPoolExecutordef process_data(data):result = np.mean(data)  # 计算数组的均值return result# 创建一个大数组
data = np.random.rand(10000000)# 将数据分割成多个子数组
sub_arrays = np.array_split(data, 4)# 配置线程池
with ThreadPoolExecutor(max_workers=4) as executor:results = list(executor.map(process_data, sub_arrays))# 计算总均值
total_mean = np.mean(results)  # 计算子数组均值的总均值print(f"总均值: {total_mean}")

2.25.2.4 优缺点

  • 优点

    • 高效管理:线程池可以高效管理和复用线程资源。
    • 简化代码:使用 concurrent.futures 可以简化多线程编程的代码。
  • 缺点

    • 线程数量限制:线程池的线程数量需要合理配置,过多或过少都会影响性能。
    • 数据同步问题:线程池中的任务需要谨慎处理数据同步问题。

2.25.3 原子操作与竞态条件

2.25.3.1 原子操作简介

原子操作(Atomic Operation)是指不会被线程调度机制打断的操作;也就是说,这个操作在执行过程中是不可分割的。在多线程编程中,原子操作可以避免竞态条件(Race Condition)。

2.25.3.2 竞态条件示例

import threadingcounter = 0def increment():global counterfor _ in range(1000000):counter += 1  # 竞态条件# 创建多个线程
threads = []
for i in range(4):thread = threading.Thread(target=increment)threads.append(thread)thread.start()# 等待所有线程完成
for thread in threads:thread.join()print(f"最终的 counter 值: {counter}")  # 预期值为 4000000,但实际值可能小于预期

2.25.3.3 使用锁解决竞态条件

import threadingcounter = 0
lock = threading.Lock()def increment():global counterfor _ in range(1000000):lock.acquire()  # 获取锁counter += 1  # 原子操作lock.release()  # 释放锁# 创建多个线程
threads = []
for i in range(4):thread = threading.Thread(target=increment)threads.append(thread)thread.start()# 等待所有线程完成
for thread in threads:thread.join()print(f"最终的 counter 值: {counter}")  # 预期值为 4000000,实际值也为 4000000

2.25.3.4 优缺点

  • 优点

    • 线程安全:使用锁可以确保操作的线程安全性。
    • 避免数据不一致:防止竞态条件导致的数据不一致问题。
  • 缺点

    • 性能开销:锁的获取和释放会增加性能开销。
    • 死锁风险:不当的锁管理可能导致死锁。

2.25.4 图像批处理案例

2.25.4.1 图像处理简介

图像处理是计算机视觉中的一个重要领域,NumPy 可以高效地处理图像数据。通过多线程并行计算,可以显著提高图像处理的性能。

2.25.4.2 图像批处理步骤

  1. 读取图像:使用 PILOpenCV 读取图像。
  2. 图像预处理:将图像数据转换为 NumPy 数组。
  3. 多线程处理:使用线程池并行处理多个图像。
  4. 结果汇总:将处理结果汇总并保存。

2.25.4.3 代码示例

import numpy as np
from concurrent.futures import ThreadPoolExecutor
from PIL import Image
import osdef load_image(file_path):image = Image.open(file_path)  # 读取图像return np.array(image)  # 将图像转换为 NumPy 数组def process_image(image):# 进行图像处理,例如滤波processed_image = np.array(image, dtype=np.float32)processed_image = np.sqrt(processed_image)  # 应用滤波操作return processed_imagedef save_image(processed_image, output_path):processed_image = np.uint8(processed_image)  # 转换为 8 位无符号整数Image.fromarray(processed_image).save(output_path)  # 保存处理后的图像def batch_process_images(input_dir, output_dir, max_workers=4):# 获取输入目录中的所有图像文件image_files = [os.path.join(input_dir, f) for f in os.listdir(input_dir) if f.endswith('.jpg')]with ThreadPoolExecutor(max_workers=max_workers) as executor:# 并行加载图像images = list(executor.map(load_image, image_files))# 并行处理图像processed_images = list(executor.map(process_image, images))# 并行保存处理后的图像for i, processed_image in enumerate(processed_images):output_path = os.path.join(output_dir, f'processed_{i}.jpg')executor.submit(save_image, processed_image, output_path)# 输入和输出目录
input_dir = 'path/to/input/images'
output_dir = 'path/to/output/images'# 批处理图像
batch_process_images(input_dir, output_dir)

2.25.4.4 注意事项

  • 文件路径:确保输入和输出目录路径正确。
  • 图像格式:处理不同格式的图像时,需要进行相应的格式转换。
  • 内存管理:处理大图像时,注意内存管理,防止内存溢出。

2.25.5 竞态条件调试

2.25.5.1 竞态条件调试工具

  • threading 模块:Python 标准库中的 threading 模块提供了基本的线程调试工具。
  • logging 模块:使用 logging 模块记录线程的执行日志,帮助调试。
  • Valgrind:C/C++ 中的 Valgrind 工具可以帮助检测线程中的内存和数据访问问题。

2.25.5.2 使用 logging 调试线程

import threading
import logginglogging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')counter = 0
lock = threading.Lock()def increment():global counterfor _ in range(1000000):lock.acquire()  # 获取锁counter += 1  # 原子操作lock.release()  # 释放锁logging.debug(f"当前 counter 值: {counter}")# 创建多个线程
threads = []
for i in range(4):thread = threading.Thread(target=increment)threads.append(thread)thread.start()# 等待所有线程完成
for thread in threads:thread.join()print(f"最终的 counter 值: {counter}")

2.25.5.3 使用 Valgrind 调试 C 代码

# 安装 Valgrind
sudo apt-get install valgrind# 编译 C 代码
gcc -g -o my_program my_program.c# 运行 Valgrind
valgrind --tool=helgrind ./my_program

2.25.5.4 优缺点

  • 优点

    • 日志记录:使用 logging 可以方便地记录线程的执行情况,帮助定位问题。
    • 问题检测Valgrind 可以检测 C 代码中的线程问题,提高代码的健壮性。
  • 缺点

    • 性能开销:日志记录和 Valgrind 会增加程序的运行开销。
    • 复杂性:调试多线程程序需要一定的技巧和经验。

总结

本文详细介绍了如何在 NumPy 中实现多线程并行计算,通过绕过 GIL 实现真正的并发。我们讨论了 NumPy 的 GIL 释放机制、线程池的配置与使用、原子操作与竞态条件的处理,以及图像批处理的案例。最后,我们还介绍了如何调试竞态条件,确保多线程程序的正确性和性能。

通过本文的学习,你将能够更好地理解和应用多线程技术,提升 Python 程序的性能。希望这些内容对你有所帮助!

参考文献

参考资料链接
NumPy 官方文档https://numpy.org/doc/stable/
Python 官方文档:threading 模块https://docs.python.org/3/library/threading.html
Python 官方文档:concurrent.futures 模块https://docs.python.org/3/library/concurrent.futures.html
Python GIL 讲解https://realpython.com/python-gil/
OpenMP 官方文档https://www.openmp.org/specifications/
Valgrind 官方文档https://valgrind.org/docs/manual/manual.html
Python 多线程优化https://www.geeksforgeeks.org/multiprocessing-vs-threading-python/
Locks in Python: The Good, the Bad, and the Uglyhttps://medium.com/@bfortuner/python-multithreading-vs-multiprocessing-730797d5fbe6
Python 中的线程安全https://www.toptal.com/python/beyond-threads-a-comprehensive-guide-to-concurrent-python
Image Processing with NumPyhttps://scikit-image.org/docs/dev/auto_examples/
图像处理基本原理https://www.cs.columbia.edu/CAVE/publications/pdfs/Brow09_TIP.pdf
Python 线程池详解https://www.jianshu.com/p/392f5b6baf44
CPython 解释器源码https://github.com/python/cpython

这篇文章包含了详细的原理介绍、代码示例、源码注释以及案例等。希望这对您有帮助。如果有任何问题请随私信或评论告诉我。

相关文章:

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.25 多线程并行:GIL绕过与真正并发

2.25 多线程并行:GIL绕过与真正并发 目录 #mermaid-svg-JO4lsTIyjOweVkos {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-JO4lsTIyjOweVkos .error-icon{fill:#552222;}#mermaid-svg-JO4lsTIyjOweVkos …...

Java 大视界 -- Java 大数据在智能医疗影像诊断中的应用(72)

💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也期待你毫无保留地分享独特见解,愿我们于此携手成长,共赴新程!💖 一、…...

【Leetcode刷题记录】1456. 定长子串中元音的最大数目---定长滑动窗口即解题思路总结

1456. 定长子串中元音的最大数目 给你字符串 s 和整数 k 。请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。 英文中的 元音字母 为(a, e, i, o, u)。 这道题的暴力求解的思路是通过遍历字符串 s 的每一个长度为 k 的子串&#xf…...

upload-labs安装与配置

前言 作者进行upload-labs靶场练习时,在环境上出了很多问题,吃了很多苦头,甚至改了很多配置也没有成功。 upload-labs很多操作都是旧时代的产物了,配置普遍都比较老,比如PHP版本用5.2.17(还有中间件等&am…...

从Transformer到世界模型:AGI核心架构演进

文章目录 引言:架构革命推动AGI进化一、Transformer:重新定义序列建模1.1 注意力机制的革命性突破1.2 从NLP到跨模态演进1.3 规模扩展的黄金定律二、通向世界模型的关键跃迁2.1 从语言模型到认知架构2.2 世界模型的核心特征2.3 混合架构的突破三、构建世界模型的技术路径3.1 …...

pytorch深度Q网络

人工智能例子汇总:AI常见的算法和例子-CSDN博客 DQN 引入了深度神经网络来近似Q函数,解决了传统Q-learning在处理高维状态空间时的瓶颈,尤其是在像 Atari 游戏这样的复杂环境中。DQN的核心思想是使用神经网络 Q(s,a;θ)Q(s, a; \theta)Q(s,…...

C++ 实现Arp断网

此程序由AI生成&#xff0c;测试过了&#xff0c;可以使用 但是&#xff0c;貌似全部都会断网 #include <pcap.h> #include <WinSock2.h> #include <iostream> #include <vector> #include <string> #include <sstream> #include <iom…...

每日一博 - 三高系统架构设计:高性能、高并发、高可用性解析

文章目录 引言一、高性能篇1.1 高性能的核心意义1.2 影响系统性能的因素1.3 高性能优化方法论1.3.1 读优化&#xff1a;缓存与数据库的结合1.3.2 写优化&#xff1a;异步化处理 1.4 高性能优化实践1.4.1 本地缓存 vs 分布式缓存1.4.2 数据库优化 二、高并发篇2.1 高并发的核心意…...

Vue2 项目中使用 Swiper

Swiper 是一个功能强大的轮播图库&#xff0c;支持触摸滑动、分页器、导航按钮等功能。本文将详细介绍如何在Vue2项目中集成Swiper 5&#xff0c;并通过watch和nextTick确保Swiper在数据加载完成后正确初始化。 1. 安装Swiper 5 首先&#xff0c;我们需要通过npm或yarn安装Sw…...

【工欲善其事】利用 DeepSeek 实现复杂 Git 操作:从原项目剥离出子版本树并同步到新的代码库中

文章目录 利用 DeepSeek 实现复杂 Git 操作1 背景介绍2 需求描述3 思路分析4 实现过程4.1 第一次需求确认4.2 第二次需求确认4.3 第三次需求确认4.4 V3 模型&#xff1a;中间结果的处理4.5 方案验证&#xff0c;首战告捷 5 总结复盘 利用 DeepSeek 实现复杂 Git 操作 1 背景介绍…...

Kafka的安装及相关操作命令

文章目录 前言一、安装kafka1. 下载kafka2. 解压kafka3. 配置环境变量 二、kafka相关命令1. 启动zk2. 启动Kafka Broker3. 创建主题4. 列出所有主题5. 查看主题详情6. 删除主题7. 启动控制台生产者8. 启动控制台消费者9. 验证结果10. 其他 前言 kafka_2.11-0.10.2.1&#xff0…...

【C++】线程池实现

目录 一、线程池简介线程池的核心组件实现步骤 二、C11实现线程池源码 三、线程池源码解析1. 成员变量2. 构造函数2.1 线程初始化2.2 工作线程逻辑 3. 任务提交(enqueue方法)3.1 方法签名3.2 任务封装3.3 任务入队 4. 析构函数4.1 停机控制 5. 关键技术点解析5.1 完美转发实现5…...

数据结构实战之线性表(三)

目录 1.顺序表释放 2.顺序表增加空间 3.合并顺序表 4.线性表之链表实现 1.项目结构以及初始代码 2.初始化链表(不带头结点) 3.链表尾部插入数据并显示 4.链表头部插入数据 5.初始化链表&#xff08;带头结点&#xff09; 6.带头结点的链表头部插入数据并显示 7.带头结…...

C#方法作用

C#方法 方法的定义与调用 首先我们要知道什么是方法,为什么要去使用方法? 方法就是把一些相关的语句组织在一起,用来执行的语句块 每一个C#程序至少有一个带有Main方法 static void Main(string[] args){ //方法的定义,一般情况下,写在一个类中// 格式: 访问修饰符 返回值…...

【python】python基于机器学习与数据分析的手机特性关联与分类预测(源码+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、测试技术。 python基于机器学习与数据分析的手机特性关联与分类…...

ZOJ 1007 Numerical Summation of a Series

原题目链接 生成该系列值的表格 对于x 的 2001 个值&#xff0c;x 0.000、0.001、0.002、…、2.000。表中的所有条目的绝对误差必须小于 0.5e-12&#xff08;精度为 12 位&#xff09;。此问题基于 Hamming (1962) 的一个问题&#xff0c;当时的大型机按今天的微型计算机标准来…...

DeepSeek-R1:开源机器人智能控制系统的革命性突破

目录 引言 一、DeepSeek-R1 的概述 1.1 什么是 DeepSeek-R1&#xff1f; 1.2 DeepSeek-R1 的定位 二、DeepSeek-R1 的核心特性 2.1 实时控制能力 2.2 多传感器融合 2.3 路径规划与导航 2.4 人工智能集成 2.5 开源与模块化设计 2.6 跨平台支持 三、DeepSeek-R1 的技术…...

全面解析文件上传下载删除漏洞:风险与应对

在数字化转型的时代&#xff0c;文件上传、下载与删除功能已经成为各类应用程序的标准配置&#xff0c;从日常办公使用的协同平台&#xff0c;到云端存储服务&#xff0c;再到社交网络应用&#xff0c;这些功能在给用户带来便捷体验、显著提升工作效率的同时&#xff0c;也隐藏…...

【C语言深入探索】结构体详解(二):使用场景

目录 一、复杂数据的表示 二、数据的封装 三、多态的模拟 四、回调函数的实现 五、多线程编程 六、通信协议的实现和文件操作 6.1. 使用结构体实现简单通信协议 6.2. 使用结构体进行文件操作 七、图形界面编程 结构体在C语言中具有广泛的应用场景&#xff0c;以下是一…...

python日志处理logging

python日志处理logging 在项目开发中&#xff0c;日志信息是程序中必不可少的组成部分。每一种语言都有相应的日志模块&#xff0c;如java中log4j&#xff0c;而python中是通过logging模块来提供日志功能。 日志要哪些本质功能&#xff1f; 在分享日志logging模块之前&#…...

6.进程的使用方式

6.进程的使用方式 **1. 父子进程的关系****2. 进程的终止****3. 僵尸进程和孤儿进程****4. 进程资源回收****5. exec 函数族****6. system 函数****7. 练习与作业****8. 进程的退出状态****9. 进程的清理函数****10. 总结** 1. 父子进程的关系 子进程是父进程的副本&#xff1…...

结构体和类

结构体和类 C结构体中的所有默认成员函数&#xff08;如&#xff1a;构造函数&#xff09;里面可以写一切合法的代码 不单单只可eg:初始化变量{}可以用来划定变量的使用范围 eg: int main() {{int a 0;//则a只能在这个{}里面使用}return 0 ;}<<输出运算符能够直接打印C…...

蓝桥杯真题 - 子串简写 - 题解

题目链接&#xff1a;https://www.lanqiao.cn/problems/3514/learning/ 个人评价&#xff1a;难度 2 星&#xff08;满星&#xff1a;5&#xff09; 前置知识&#xff1a;前缀和 整体思路 定义 s u m i sum_i sumi​ 表示前 i i i 个字符含有字符 c 1 c_1 c1​ 的个数&…...

【大模型】AI 辅助编程操作实战使用详解

目录 一、前言 二、AI 编程介绍 2.1 AI 编程是什么 2.1.1 为什么需要AI辅助编程 2.2 AI 编程主要特点 2.3 AI编程底层核心技术 2.4 AI 编程核心应用场景 三、AI 代码辅助编程解决方案 3.1 AI 大模型平台 3.1.1 AI大模型平台代码生成优缺点 3.2 AI 编码插件 3.3 AI 编…...

RK3566-移植5.10内核Ubuntu22.04

说明 记录了本人使用泰山派&#xff08;RK3566&#xff09;作为平台并且成功移植5.10.160版本kernel和ubuntu22.04&#xff0c;并且成功配置&连接网络的完整过程。 本文章所用ubuntu下载地址&#xff1a;ubuntu-cdimage-ubuntu-base-releases-22.04-release安装包下载_开源…...

从零开始实现一个双向循环链表:C语言实战

文章目录 1链表的再次介绍2为什么选择双向循环链表&#xff1f;3代码实现&#xff1a;从初始化到销毁1. 定义链表节点2. 初始化链表3. 插入和删除节点4. 链表的其他操作5. 打印链表和判断链表是否为空6. 销毁链表 4测试代码5链表种类介绍6链表与顺序表的区别7存储金字塔L0: 寄存…...

PostgreSQL 数据库模式基础操作

查看数据库或者使用pgAdmin或者QGIS查看PG数据库时&#xff0c;可以看到数据库名下面有一个Public&#xff0c;然后才是具体的表&#xff0c;搜索了一下&#xff0c;按照PG官网&#xff1a;https://www.postgresql.org/docs/current/ddl-schemas.html 的说明&#xff0c;这个Pu…...

51单片机 06 定时器

51 单片机的定时器属于单片机的内部资源&#xff0c;其电路的连接和运转均在单片机内部完成。 作用&#xff1a;1、用于计时&#xff1b;2、替代长时间的Delay&#xff0c;提高CPU 运行效率和处理速度。 定时器个数&#xff1a;3个&#xff08;T0、T1、T2&#xff09;&#xf…...

CSS 值和单位详解:从基础到实战

CSS 值和单位详解&#xff1a;从基础到实战 1. 什么是 CSS 的值&#xff1f;示例代码&#xff1a;使用颜色关键字和 RGB 函数 2. 数字、长度和百分比2.1 长度单位绝对长度单位相对长度单位 2.2 百分比 3. 颜色3.1 颜色关键字3.2 十六进制 RGB 值3.3 RGB 和 RGBA 值3.4 HSL 和 H…...

【C++】P1957 口算练习题

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述输入格式&#xff1a;输出格式&#xff1a; &#x1f4af;我的做法代码实现&#xff1a; &#x1f4af;老师的做法代码实现&#xff1a; &#x1f4af;对比分析&am…...