python:并发编程(十八)

news2024/12/23 17:21:22

前言

本文将和大家一起探讨python并发编程的实际运用,会以一些我实际使用的案例,或者一些典型案例来分享。本文使用的案例是我实际使用的案例(下篇),是基于之前效率不高的代码改写成并发编程的。让我们来看看改造的过程,这样就会对并发编程的高效率有个清晰地认知,也会在改造过程中学到一些知识。

本文为python并发编程的第十八篇,上一篇文章地址如下:

python:并发编程(十七)_Lion King的博客-CSDN博客

下一篇文章地址如下:

(暂无)
 

一、实施方案:有没有可能image_gray函数也可以优化

通过前两个章节,我们优化了assert_run函数,并且通过进程优化了一些时间,当然耗时最多的部分image_gray函数并没有被优化。本章将一起探讨该部分的优化。

1、image_gray是否可以通过并发优化

我们先看一下该函数运用到的2个for循环,先用其他for循环模拟他使用的时间,即如下一个简单的函数:

import time


st=time.time()

for i in range(1000):
    for j in range(2000):
        print(i*j)

et=time.time()
print(et-st)

上述函数,在没有并发的情况下耗时16.6秒。

2、多线程改造2个for循环

(1)单线程是花的时间为39秒左右

import concurrent.futures
import time

def process(i, j):
    result = i * j
    return result

st=time.time()
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
    futures = []
    for i in range(1000):
        for j in range(2000):
            future = executor.submit(process, i, j)
            futures.append(future)

    # 获取结果
    for future in concurrent.futures.as_completed(futures):
        result = future.result()
        print(result)
et=time.time()
print(et-st)

(2)10个线程是花的时间为60秒左右

3、多进程改造2个for循环

(1)单进程是花的时间为502秒左右

import concurrent.futures
import time

def process(i, j):
    result = i * j
    return result

if __name__ == "__main__":
    st=time.time()
    with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor:
        futures = []
        for i in range(1000):
            for j in range(2000):
                future = executor.submit(process, i, j)
                futures.append(future)

        # 获取结果
        for future in concurrent.futures.as_completed(futures):
            result = future.result()
            print(result)
    et=time.time()
    print(et-st)

 (2)10个进程是花的时间为520秒左右

二、来自两个for循环的一点点思考

1、为什么两个for循环不加线程时只花费16秒。加了线程花费60秒?

如果在两个嵌套的 for 循环中添加了线程,并且在每次迭代中启动一个新的线程来执行计算任务,这可能导致执行时间增加的原因有以下几点:

(1)线程创建和销毁开销:创建和销毁线程都会带来一定的开销。如果在每次迭代中都创建新的线程,那么线程的创建和销毁开销将会成为性能瓶颈,从而增加整体执行时间。

(2)线程切换开销:在线程之间进行切换也需要一定的开销。如果线程切换的次数过多,例如在每次迭代中都切换线程,那么线程切换的开销会累积并导致执行时间增加。

(4)GIL(全局解释器锁)限制:如果你使用的是 CPython 解释器,并且每个线程都在执行 CPU 密集型任务(如计算),那么由于 GIL 的存在,同一时间只能有一个线程执行 Python 字节码,其他线程将被阻塞。这就意味着多个线程无法真正并行执行计算任务,反而会带来额外的线程切换开销,从而增加整体执行时间。

综上所述,如果添加线程的目的是为了加速计算任务,但是在 CPU 密集型场景中,由于线程的创建和销毁开销、线程切换开销以及 GIL 的限制,可能会导致执行时间增加。在这种情况下,考虑使用其他并发模型,如多进程或异步编程,可以更好地利用计算资源和提高性能。

2、为什么两个for循环不加进程时只花费16秒。加了进程花费500秒?

如果在两个嵌套的 for 循环中添加了进程,并且在每次迭代中启动一个新的进程来执行计算任务,这可能导致执行时间增加的原因有以下几点:

(1)进程创建和销毁开销:创建和销毁进程都会带来较大的开销。如果在每次迭代中都创建新的进程,那么进程的创建和销毁开销将会成为性能瓶颈,从而增加整体执行时间。

(2)进程间通信开销:在多进程编程中,进程之间需要进行通信和同步操作。这包括数据传输、共享内存等,而这些操作会引入一定的开销。在每次迭代中都涉及进程间通信,会增加额外的开销,导致执行时间增加。

(3)上下文切换开销:在多进程编程中,由于操作系统需要进行进程间的上下文切换,这也会引入一定的开销。在每次迭代中都涉及进程的切换,会增加上下文切换的次数,从而增加执行时间。

(4)全局解释器锁(GIL)限制:如果你使用的是 CPython 解释器,并且每个进程都在执行 CPU 密集型任务(如计算),那么由于 GIL 的存在,同一时间只能有一个进程执行 Python 字节码,其他进程将被阻塞。这就意味着多个进程无法真正并行执行计算任务,反而会带来额外的进程切换和通信开销,从而增加整体执行时间。

综上所述,如果添加进程的目的是为了加速计算任务,但是在 CPU 密集型场景中,由于进程的创建和销毁开销、进程间通信开销、上下文切换开销以及 GIL 的限制,可能会导致执行时间增加。在这种情况下,考虑使用其他并发模型,如多线程或异步编程,可以更好地利用计算资源和提高性能。

3、两个for循环,10个进程花费500秒,10个线程花费60秒,为什么进程比线程慢这么多?

进程和线程的性能差异可能受到以下几个因素的影响:

(1)资源消耗:进程的创建和销毁通常比线程更昂贵,因为进程之间需要独立的内存空间和系统资源。创建和销毁进程需要更多的开销,包括内存分配、文件描述符的复制和清理等。因此,在每次迭代中创建和销毁多个进程可能会导致较高的资源消耗,从而增加执行时间。

(2)上下文切换开销:进程之间的上下文切换比线程之间的上下文切换更昂贵。上下文切换是指操作系统从一个执行单元(进程或线程)切换到另一个执行单元的过程。在多进程情况下,由于进程之间独立的内存空间和资源,上下文切换需要更多的开销,包括保存和恢复进程的状态等。因此,在每次迭代中进行多个进程的切换可能会导致较高的上下文切换开销,从而增加执行时间。

(3)全局解释器锁(GIL):如果你使用的是 CPython 解释器,并且每个线程都在执行 CPU 密集型任务(如计算),那么由于 GIL 的存在,同一时间只能有一个线程执行 Python 字节码,其他线程将被阻塞。但是,在多线程情况下,线程切换的开销通常比进程切换的开销小得多。因此,当涉及到 CPU 密集型任务时,线程可以更好地利用计算资源,而进程由于 GIL 的限制可能无法充分利用多核处理器,导致性能较低。

综上所述,进程比线程慢的原因可能是进程的创建和销毁开销、进程间的上下文切换开销以及全局解释器锁(GIL)的影响。在特定场景下,线程可能更适合执行 CPU 密集型任务,而进程适合执行 I/O 密集型任务或并行处理。然而,对于不同的应用和环境,性能差异可能会有所不同,因此选择合适的并发模型需要考虑具体的需求和条件。

三、使用其他方式优化image_gray函数

1、原函数优化

原函数的代码是用于计算一张图片的平均灰度值。它通过打开图片,遍历每个像素点并计算其灰度值,最后求得平均灰度值并返回。

代码中的逻辑是正确的,但为了提高计算效率,可以考虑使用一些优化技巧,如减少不必要的循环次数、使用向量化计算等。咋们改造如下:

def image_gray(img):
    # 打开图片
    img = Image.open(img)

    # 将图像转换为灰度图
    img_gray = img.convert("L")

    # 获取图像的像素数据
    pixels = list(img_gray.getdata())

    # 计算平均灰度值
    total_gray = sum(pixels)
    avg_gray = total_gray / len(pixels)

    return avg_gray

在优化后的代码中,主要做了以下改进:

(1)使用图像的 convert 方法将图像转换为灰度图像,避免了在循环中判断像素的模式。

(2)使用 getdata 方法获取图像的像素数据,返回一个包含所有像素值的列表。

(3)使用 sum 函数计算像素值的总和,而不需要显式地进行循环累加。

(4)直接使用列表的长度作为像素点的总数,避免了在循环中进行计数。

这些优化措施可以提高计算效率和性能。注意,具体的优化方式还取决于图像的大小和处理需求,可以根据实际情况进行调整和改进。

2、优化效果

单线程的情况下,处理40张图片只花了4秒钟。10个进程的情况下,只花不到1秒,至此,本项目优化结束。

import os
from PIL import Image
import time
import concurrent.futures

def calculate_runtime(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        runtime = end_time - start_time
        print(f"程序运行时间:{runtime}秒")
        return result
    return wrapper

# @calculate_runtime
# def image_gray(img):
#     # 打开图片
#     img = Image.open(img)
#
#     # 计算平均灰度值
#     gray_sum = 0
#     count = 0
#     for x in range(img.width):
#         for y in range(img.height):
#             if img.mode == "RGB":
#                 r, g, b = img.getpixel((x, y))
#                 gray_sum += (r + g + b) / 3
#             elif img.mode == "L":
#                 gray_value = img.getpixel((x, y))
#                 gray_sum += gray_value
#             count += 1
#
#     avg_gray = gray_sum / count
#     return avg_gray

@calculate_runtime
def image_gray(img):
    # 打开图片
    img = Image.open(img)

    # 将图像转换为灰度图
    img_gray = img.convert("L")

    # 获取图像的像素数据
    pixels = list(img_gray.getdata())

    # 计算平均灰度值
    total_gray = sum(pixels)
    avg_gray = total_gray / len(pixels)

    # print(avg_gray)

    return avg_gray



@calculate_runtime
def find_image(folder_path):
    # 定义一个列表存储图片路径
    images = []
    # 遍历文件夹下的所有文件
    for root, dirs, files in os.walk(folder_path):
        for file in files:
            file_path = os.path.join(root, file)
            # 处理每个文件,将其添加到列表中
            images.append(file_path)
    return images[0:40]

def process_image(img):
    gray = image_gray(img)
    # 灰度值小于50,将认为是黑图
    if gray < 50:
        print(img, ":", gray)

@calculate_runtime
def assert_run(folder_path):
    images = find_image(folder_path)

    with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor:
        # 提交任务并发执行
        futures = [executor.submit(process_image, img) for img in images]

        # 等待所有任务完成
        concurrent.futures.wait(futures)


if __name__ == "__main__":
    folder_path = r'C:\Users\yeqinfang\Desktop\临时文件\文件存储'
    assert_run(folder_path)




 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/676296.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算逆波兰表达式

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;数据结构。数据结构专栏主要是在讲解原理的基础上拿Java实现 ⭐码云地址超链接(Gitee)&#xff1a;这里存放我学…

如何搭建Nginx网站服务

目录 一、首先搭建Nginx服务 二、授权的访问控制 第一步 安装依赖包 第二步 生成用户密码认证文件 第三步 修改文件属性和权限 第四步 修改配置文件 第五步 用浏览器测试网站 三、基于IP地址进行限制 第一步 修改配置文件 第二步 用两台设备进行访问测试 四、基于域…

工人规范操作识别系统 yolov5

工人规范操作识别系统通过yolov5python网络模型技术&#xff0c;工人规范操作识别系统对工人的操作进行实时监测&#xff0c;当工人规范操作识别系统检测到工人操作不符合规范时&#xff0c;将自动发出警报提示相关人员采取措施。YOLOv5中在训练模型阶段仍然使用了Mosaic数据增…

HCI-1

3.1 定义 就本文档而言&#xff0c;适用以下术语和定义&#xff1a; 嵌入式安全元件主机&#xff1a;在不可移动安全元件中实现的主机 门&#xff1a;主机内部运行的服务的入口点 主机&#xff1a;运行一项或多项服务的逻辑实体 主机控制器&#xff1a;还负责管理主机网络的…

聊聊 分布式系统 中的补偿机制设计问题

一、关于业务补偿机制 1、什么是业务补偿 2、业务补偿设计的实现方式 二、关于回滚 1、显示回滚 2、回滚的实现方式 三、关于重试 1、重试的使用场景 2、重试策略 3、重试时的注意事项 四、业务补偿机制的注意事项 1、ACID 还是 BASE 2、业务补偿设计的注意事项 我们知…

Langchain+本地大语言模型进行数据库操作的实战代码

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

Navicat Premium 16执行.sql语句中含有汉字乱码造成view和function创建后无法使用

Navicat Premium 16执行.sql语句中含有汉字乱码造成view和function创建后无法使用 如图&#xff0c;从这里选择sql时没法改sql。所以造成我昨天创建view和function时创建好的前面有感叹号没法用。打开一个fun看里面的汉字是问号。 所以要从这里打开&#xff1a; 1. ultraedit…

EMC学习笔记(十)特殊信号的EMC处理(二)

特殊信号的EMC处理&#xff08;二&#xff09; 1.对外接口的EMC设计标准电路1.1 DVI EMC设计标准电路1.2 HDMI接口EMC设计标准电路1.3 LVDS接口EMC设计标准电路1.4 PS2接口EMC设计标准电路1.5 RJ11 EMC设计标准电路1.6 SCART接口EMC设计标准电路1.7 s-video接口EMC设计标准电路…

五个步骤,助你优雅的写好 Controller 层代码!

Controller 层逻辑 普通写法 优化思路 Controller 层逻辑 MVC架构下&#xff0c;我们的web工程结构会分为三层&#xff0c;自下而上是dao层&#xff0c;service层和controller层。controller层为控制层&#xff0c;主要处理外部请求&#xff0c;调用service层。 一般情况下…

6.23黄金是否会跌破1900?多单被套怎么办?

近期有哪些消息面影响黄金走势&#xff1f;今日黄金多空该如何研判&#xff1f; ​黄金消息面解析&#xff1a;周四&#xff08;6月22日&#xff09;美市尾盘&#xff0c;现货黄金收报1910美元/盎司&#xff0c;下跌20美元或0.1%&#xff0c;日内最高触及1934.95美元/盎司&…

C++ 面向对象(1)——类 对象

C 在 C 语言的基础上增加了面向对象编程&#xff0c;C 支持面向对象程序设计。类是 C 的核心特性&#xff0c;通常被称为用户定义的类型。 类用于指定对象的形式&#xff0c;是一种用户自定义的数据类型&#xff0c;它是一种封装了数据和函数的组合。类中的数据称为成员变量&a…

Studio One6中文版多少钱?有哪些新功能

Studio One6中文版现在有三个版本&#xff0c;免费版&#xff0c;Artist&#xff0c;Pro版本。下载后是免费版&#xff0c;免费版没有时间限制&#xff0c;但是功能受限。三个版本都支持win/mac系统&#xff0c;而且同时支持5台设备使用&#xff0c;还可以换机使用。 三个版本…

Spring Cloud Day2 Nacos配置管理、Feign远程调用与Gateway服务网关

SpringCloud实用篇02 0.学习目标 1.Nacos配置管理 Nacos除了可以做注册中心&#xff0c;同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多&#xff0c;达到数十、数百时&#xff0c;逐个修改微服务配置就会让人抓狂&#xff0c;而且很容易出错。我…

关闭 MAC 的 Microsoft AutoUpdate 自动更新

不是我说&#xff0c;这玩意儿看着是真不爽&#xff01;&#xff01;而且每天都要弹出来搞事情&#xff01;&#xff01;&#xff01; 我宣布&#xff1a;今天就要永久关闭 MAC 的 Microsoft AutoUpdate 自动更新&#xff01;&#xff01; 像我一样的朋友请举手&#xff01;&am…

Linux学习[17]bash学习深入3---万用字符特殊符号---数据流重导向

文章目录 前言1. 万用字符2. 特殊字符3. 数据流重导向3.1标准输出3.2 标准输入 总结 前言 这篇博客是对之前在查找的时候涉及到的一些通配符(bash里面就是万用字符)的整理。这个为后面管线相关打一个基础。 1. 万用字符 这里整理了一个表格&#xff0c;后面配上相关实例。 符…

定制化你的应用外观:gradio的自定义主题功能

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

Axure教程——多项选择器

本文介绍利用Axure里的中继器和动态面板制作一个多选下拉列表 一、效果 预览地址:https://frh0rc.axshare.com 二、功能 1、点击下拉框可以弹出选项&#xff0c;点击选项可以选中选项2、用户可以取消选中 三、制作 1、制作下拉框 拖入一个矩形组件&#xff0c;命名为“下拉框…

C++——指针空值

在良好的C/C编程习惯中&#xff0c;声明一个变量时最好给该变量一个合适的初始值&#xff0c;否则可能会出现不可预料的错误&#xff0c;比如未初始化的指针。如果一个指针没有合法的指向&#xff0c;我们基本都是按照如下方式对其进行初始化&#xff1a; void TestPtr() {int*…

python---案例分析(2)

例5: 使用python生成一个二维码 结果就会显示一个二维码!拿出手机扫描二维码就可以看到make中填写的内容! 例6: 操作excel 使用python计算平均分的情况 首先在自己的pycharm上安装xlrd 必须是上述版本的 安装成功版本后,import一下即可使用 以下是计算100班的平均分 例6: …

后端开发通用

1、前后端开发 项目基于前后端分离的架构进行开发&#xff0c;前后端分离架构总体上包括前端和服务端&#xff0c;通常是多人协作开发 对于后端java工程师 把精力放在设计模式&#xff0c;springspringmvc&#xff0c;linux&#xff0c;mysql事务隔离与锁机制&#xff0c;mongo…