【Python】教你彻底了解Python中的并发编程

​​​​在这里插入图片描述

文章目录

    • 一、并发编程的基本概念
      • 1. 线程(Thread)
      • 2. 进程(Process)
      • 3. 协程(Coroutine)
    • 二、Python中的线程与进程
      • 1. 线程
        • 1.1 创建和启动线程
        • 1.2 线程同步
      • 2. 多进程
        • 2.1 创建和启动进程
        • 2.2 进程间通信
    • 三、异步编程
      • 1. 协程
        • 1.1 定义和调用协程
      • 2. 异步任务
    • 四、并发编程的实际应用示例
      • 1. 多线程Web爬虫
      • 2. 多进程图像处理
    • 五、并发编程的注意事项
      • 1. 线程安全
      • 2. 死锁
      • 3. 资源消耗
      • 4. 异常处理
    • 结论

随着计算机处理能力的不断提升和多核处理器的普及,并发编程变得越来越重要。并发编程可以使程序同时执行多个任务,从而提高程序的性能和响应速度。在Python中,有多种方式可以实现并发编程,包括线程、进程和异步编程。在这篇文章中,我们将深入探讨Python中的并发编程,涵盖其基本概念、线程与进程、多线程编程、多进程编程、异步编程以及并发编程的实际应用示例。

一、并发编程的基本概念

并发编程是指同时执行多个任务的编程技术。并发编程的目标是提高程序的执行效率和响应速度。在并发编程中,有几个重要的概念需要理解:

1. 线程(Thread)

线程是程序执行的最小单位。一个进程可以包含多个线程,每个线程可以执行不同的任务。线程之间可以共享内存,因此线程之间的通信比较容易,但需要注意线程安全问题。

2. 进程(Process)

进程是程序执行的一个实例。每个进程有自己独立的内存空间,相互之间不会影响。多进程编程可以有效利用多核处理器,但进程之间的通信相对复杂。

3. 协程(Coroutine)

协程是一种比线程更轻量级的并发实现方式。协程在执行过程中可以暂停和恢复,从而实现非阻塞的并发执行。Python中的异步编程通常使用协程来实现。

二、Python中的线程与进程

Python中提供了多种实现并发编程的方式,包括线程、多进程和异步编程。我们将分别介绍这些方法,并讨论它们的优缺点。

1. 线程

Python中的threading模块提供了创建和管理线程的功能。通过threading.Thread类,我们可以创建和启动线程。

1.1 创建和启动线程

以下示例展示了如何创建和启动线程:

import threadingdef print_numbers():for i in range(1, 6):print(i)# 创建线程
thread = threading.Thread(target=print_numbers)# 启动线程
thread.start()# 等待线程结束
thread.join()
print("Thread execution completed.")

在上面的例子中,我们创建了一个线程来执行print_numbers函数,并启动了线程。join方法用于等待线程执行完成。

1.2 线程同步

在多线程编程中,多个线程可能会同时访问共享资源,从而导致数据不一致的问题。为了避免这种情况,我们可以使用线程同步机制,如锁(Lock)。

以下示例展示了如何使用锁来同步线程:

import threadingcounter = 0
lock = threading.Lock()def increment_counter():global counterfor _ in range(1000000):with lock:counter += 1# 创建和启动线程
threads = []
for _ in range(5):thread = threading.Thread(target=increment_counter)threads.append(thread)thread.start()# 等待所有线程结束
for thread in threads:thread.join()print(f"Final counter value: {counter}")

在上面的例子中,我们使用锁来确保每次只有一个线程可以访问和修改counter变量,从而避免数据不一致的问题。

2. 多进程

Python中的multiprocessing模块提供了创建和管理进程的功能。多进程编程可以有效利用多核处理器,提高程序的执行效率。

2.1 创建和启动进程

以下示例展示了如何创建和启动进程:

import multiprocessingdef print_numbers():for i in range(1, 6):print(i)# 创建进程
process = multiprocessing.Process(target=print_numbers)# 启动进程
process.start()# 等待进程结束
process.join()
print("Process execution completed.")

在上面的例子中,我们创建了一个进程来执行print_numbers函数,并启动了进程。join方法用于等待进程执行完成。

2.2 进程间通信

进程之间有独立的内存空间,因此进程间通信相对复杂。Python的multiprocessing模块提供了多种进程间通信的机制,如队列(Queue)和管道(Pipe)。

以下示例展示了如何使用队列进行进程间通信:

import multiprocessingdef producer(queue):for i in range(5):queue.put(i)print(f"Produced: {i}")def consumer(queue):while True:item = queue.get()if item is None:breakprint(f"Consumed: {item}")# 创建队列
queue = multiprocessing.Queue()# 创建和启动生产者和消费者进程
producer_process = multiprocessing.Process(target=producer, args=(queue,))
consumer_process = multiprocessing.Process(target=consumer, args=(queue,))producer_process.start()
consumer_process.start()# 等待生产者进程结束
producer_process.join()# 发送终止信号给消费者进程
queue.put(None)# 等待消费者进程结束
consumer_process.join()
print("Producer and Consumer execution completed.")

在上面的例子中,我们使用队列来在生产者和消费者进程之间传递数据。

三、异步编程

异步编程是一种高效的并发编程方式,尤其适用于I/O密集型任务。Python中的asyncio模块提供了异步编程的支持,可以使用协程实现非阻塞的并发执行。

1. 协程

协程是一种可以在执行过程中暂停和恢复的函数。协程使用async def关键字定义,使用await关键字调用。

1.1 定义和调用协程

以下示例展示了如何定义和调用协程:

import asyncioasync def print_numbers():for i in range(1, 6):print(i)await asyncio.sleep(1)# 获取事件循环
loop = asyncio.get_event_loop()# 运行协程
loop.run_until_complete(print_numbers())
loop.close()
print("Coroutine execution completed.")

在上面的例子中,我们定义了一个协程print_numbers,并在事件循环中运行它。await asyncio.sleep(1)用于模拟异步操作。

2. 异步任务

在异步编程中,我们可以使用asyncio.create_task创建异步任务,并使用await等待任务完成。

以下示例展示了如何创建和管理异步任务:

import asyncioasync def print_numbers():for i in range(1, 6):print(i)await asyncio.sleep(1)async def main():# 创建异步任务task1 = asyncio.create_task(print_numbers())task2 = asyncio.create_task(print_numbers())# 等待所有任务完成await asyncio.gather(task1, task2)# 获取事件循环并运行主协程
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
print("All tasks completed.")

在上面的例子中,我们创建了两个异步任务,并使用asyncio.gather等待它们全部完成。

四、并发编程的实际应用示例

并发编程在实际应用中有广泛的应用,以下是两个实际应用示例,演示如何使用Python进行并发编程。

1. 多线程Web爬虫

以下示例演示如何使用多线程实现一个简单的Web爬虫:

import threading
import requests
from bs4 import BeautifulSoupurls = ['https://www.example.com/page1','https://www.example.com/page2','https://www.example.com/page3'
]def fetch_url(url):response = requests.get(url)soup = BeautifulSoup(response.text, 'html.parser')title = soup.title.stringprint(f"Title of {url}: {title}")# 创建和启动线程
threads = []
for url in urls:thread = threading.Thread(target=fetch_url, args=(url,))threads.append(thread)thread.start()# 等待所有线程结束
for thread in threads:thread.join()print("All URLs fetched.")

在上面的例子中,我们使用多线程来并发获取多个网页的标题,从而提高爬取效率。

2. 多进程图像处理

以下示例演示如何使用多进程实现并行图像处理:

import multiprocessing
from PIL import Image, ImageFilterimage_paths = ['image1.jpg','image2.jpg','image3.jpg'
]def process_image(image_path):image = Image.open(image_path)image = image.filter(ImageFilter.GaussianBlur(5))output_path = f"processed_{image_path}"image.save(output_path)print(f"Processed {image_path} and saved as {output_path}")# 创建和启动进程
processes = []
for image_path in image_paths:process = multiprocessing.Process(target=process_image, args=(image_path,))processes.append(process)process.start()# 等待所有进程结束
for process in processes:process.join()print("All images processed.")

在上面的例子中,我们使用多进程来并行处理多个图像,从而提高图像处理的效率。

五、并发编程的注意事项

并发编程可以显著提高程序的性能,但同时也带来了一些挑战和问题。以下是一些并发编程的注意事项:

1. 线程安全

在多线程编程中,多个线程可能会同时访问共享资源,从而导致数据不一致的问题。为了避免这种情况,我们需要使用线程同步机制,如锁(Lock)、信号量(Semaphore)等。

2. 死锁

死锁是指两个或多个线程互相等待对方释放资源,从而导致程序无法继续执行的情况。为了避免死锁,我们需要小心设计线程同步机制,避免循环等待的情况。

3. 资源消耗

线程和进程的创建和管理会消耗系统资源。在实际应用中,我们需要合理控制线程和进程的数量,避免资源浪费。

4. 异常处理

在并发编程中,异常处理尤为重要。我们需要捕获和处理并发任务中的异常,确保程序的稳定性和可靠性。

结论

并发编程是现代编程中的一个重要技术,可以显著提高程序的性能和响应速度。在Python中,有多种方式可以实现并发编程,包括线程、多进程和异步编程。通过理解和掌握这些技术,我们可以编写出更加高效和可靠的程序。在本文中,我们详细讨论了并发编程的基本概念、Python中的线程与进程、多线程编程、多进程编程、异步编程以及并发编程的实际应用示例。希望这篇文章能帮助你更好地理解和应用Python中的并发编程技术,从而在实际项目中获得更多的性能提升和优化效果。

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

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

相关文章

【Python数据预处理系列】掌握数据清洗技巧:如何高效使用drop()函数去除不需要的列

目录 一、准备数据 二、使用drop函数去除掉指定列 在数据分析和预处理的过程中,经常会遇到需要从数据集中移除某些列的情况。本文将引导您了解如何使用drop函数高效地去除不需要的列,帮助您提升数据处理技能,确保您的数据集只包含对分析有价…

MYSQL ORDER BY

在MySQL中,默认情况下,升序排序会将NULL值放在前面,因为在排序过程中,NULL会被视为最小值。然而,有时会要求在升序排序中需要将NULL值放在最后。 例如根据日期升序时就会出现这种问题 方案一: SELECT sor…

【PostgreSQL 小课】日志及审计 01:日志

日志及审计 01:日志 以下内容是来自于我的知识星球:《PostgreSQL 小课》专栏,有需要可以关注一下 PostgreSQL 提供了非常丰富的日志基础设施。能够检查日志是每个 DBA 的关键技能——日志提供了关于集群过去的操作、当前正在进行的操作以及发…

数据虚拟化:零数据搬运,实现全域数据的集成和自适应加速

数据虚拟化技术的兴起,与传统数据仓库体系的弊端日益显现有着密切关系。 过去,企业通常会构建数据仓库来存储与加工结构化数据。数据仓库虽然实现了数据的物理集中存储,但过于依赖大量的 ETL 工程师来支持数据的集成、准备、开发与管理。随着…

uniapp小程序src引用服务器图片时全局变量与图片路径拼接

理论上&#xff0c;应该在main.js中定义一个全局变量&#xff0c;然后在页面的<image>标签上的是src直接使用即可 main.js 页面上 看上去挺靠谱的&#xff0c;实际上小程序后台会报一个错 很明显这种方式小程序是不认的&#xff0c;这就头疼了&#xff0c;还想过另外一个…

宝藏级-LLM-文档级别向量化问答技术总结

简单且详细的目录 1.简单阶段描述2.阶段展开描述2.1.第一阶段技术:加载文档-读取文档-文本分割(Text splitter)2.1.1.加载读取文档:读取加载的文档内容,通常是将其转化为文本格式2.1.1.1.基于文档解析工具的方法2.1.1.1.1.pdf解析工具2.1.1.1.2.doc、docx解析工具2.1.1.1.3.…

Android 蓝牙概述

一、什么是蓝牙 蓝牙是一种短距离&#xff08;一般10m内&#xff09;无线通信技术。蓝牙技术允许固定和移动设备在不需要电缆的情况下进行通信和数据传输。 “蓝牙”这名称来自10世纪的丹麦国王哈拉尔德(Harald Gormsson)的外号。出身海盗家庭的哈拉尔德统一了北欧四分五裂的国…

【js】input设置focus()不生效

实现功能&#xff1a;点击添加文章标签的时候&#xff0c;输入框聚焦。 页面上&#xff0c;input输入框默认不显示&#xff0c;是display:none; 点击添加按钮后&#xff0c;input输入框才显示。 在js里面直接获取元素进行设置聚焦不成功 。 ∵ focus方法比show方法先执行。j…

docker目录挂载失败:Check if the specified host path exists and is the expected type

docker目录挂载失败&#xff1a;Check if the specified host path exists and is the expected type docker目录挂载命令&#xff0c;其目的是为了达到修改linux上的文件同步到容器上&#xff0c;从而实现修改容器的配置文件。 在docker目录挂载或启动容器时报错&#xff0c…

spring源码初始学习基础-环境

环境&#xff1a;在这里插入代码片 allprojects {repositories {maven { url file:///D:/software/repository} // 本地仓库地址&#xff0c;如果没有依次向下寻找maven { url "https://maven.aliyun.com/repository/public" }mavenLocal()mavenCentral()}buildscri…

2年JAVA今日头条3轮面试经历

面头条的时候已经是十月底了。大半个月没有面试&#xff0c;之前准备的知识点比如http状态码之类的记忆性的东西&#xff0c;早就忘光了。 二面的时候问了一堆状态码&#xff0c;全都不记得了。面试官态度很好&#xff0c;跟我说&#xff0c;你现在不记得了&#xff0c;说明你…

【Vue】组件通信

文章目录 一、组件之间如何通信二、组件关系分类三、通信解决方案四、父子通信流程五、父向子通信代码示例六、子向父通信代码示例 组件通信&#xff0c;就是指组件与组件之间的数据传递 组件的数据是独立的&#xff0c;无法直接访问其他组件的数据。想使用其他组件的数据&…

利用人工智能-智慧合同管理系统规避企业合同管理风险

随着企业业务的不断扩展&#xff0c;合同管理在企业运营中扮演着越来越重要的角色。然而&#xff0c;传统的合同管理方法往往存在效率低下、风险控制不足等问题。为了解决这些问题&#xff0c;道本科技智慧合同管理系统不仅通过合同全生命周期的管理对企业合同管理从宏观上做了…

开发uniapp 小程序时遇到的问题

1、【微信开发者工具报错】routeDone with a webviewId XXX that is not the current page 解决方案: 在app.json 中添加 “lazyCodeLoading”: “requiredComponents” uniapp的话加到manifest.json下的mp-weixin 外部链接文章&#xff1a;解决方案文章1 解决方案文章2 &qu…

SEACells:元细胞分析

元细胞是从单细胞测序数据中衍生的细胞分组&#xff0c;代表高度精细的不同细胞状态。在这里&#xff0c;作者介绍了单细胞细胞状态聚集 (SEACells)&#xff0c;这是一种用于识别元细胞的算法&#xff0c;它克服了单细胞数据的稀疏性&#xff0c;同时保留了传统细胞聚类所掩盖的…

【C++进阶】深入STL之vector:深入研究迭代器失效及拷贝问题

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;初步了解vector &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀STL之vector &#x1f4d2;1. 迭…

Linux C语言:数组的定义和初始化

一、数组 1、定义 在程序设计中&#xff0c;为了处理方便&#xff0c;把具有相同类型的若干变量按有序的形式组织起来&#xff0c;具有一定顺序关系的若干个变量的集合就是数组 。 2、特点 组成数组的各个变量称为数组的元素数组中各元素的数据类型要求相同元素在内存中是连…

pdf的压缩该怎么做?快速在线压缩pdf的方法

pdf文件是现在很常用的一种文件格式&#xff0c;有很多的文件内容都可以通过这种格式来展示内容&#xff0c;比如一些通知文件、设计图、个人信息等等&#xff0c;文件的内容越多就会越大&#xff0c;在使用的时候经常会受到一定的限制。那么有什么方法能够快速的将pdf文件变小…

目标检测-AnyLabeling标注格式转换成YOLO格式

Anylabel可以极大的增加数据的标注效率&#xff0c;但是其标注格式如何能转换成YOLO标注格式&#xff0c;具体内容如下所示。 关于AnyLabeling的其它详细介绍如下链接所示 https://blog.csdn.net/u011775793/article/details/134918861 Github链接 https://github.com/vietanhd…

巧用docker+jmeter快速实现分布式百万级并发

分享背景 碰到的问题&#xff1a; 一个JMeter实例可能无法产生足够的负载来对你的应用程序进行压力测试&#xff5e; 解决办法&#xff1a; 1、修改jmeter配置文件里的内存堆 2、引入jmeter分布式压测 带来的问题&#xff1a; 如果我们要做分布式负载测试–我们需要1个…