Python多线程与多进程:编织并发的魔法纹章

2023-12-14 11:52

本文主要是介绍Python多线程与多进程:编织并发的魔法纹章,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 写在开头
  • 1.理解并发编程的基本概念
    • 1.1 并发与并行的区别
    • 1.2 并发编程的目标
    • 1.3 并发编程的挑战
    • 1.4 并发编程的实现方式
    • 1.5 并发编程的选择
  • 2.魔法的交织:使用`threading`和`multiprocessing`模块
    • 2.1 编织多线程的咒语
    • 2.2 简单的并发编程示例
  • 3.并发的魔法表演
    • 3.1 共享数据与同步
      • 3.1.1 共享数据
      • 3.1.2 锁的应用
    • 3.2 进程间通信
      • 3.2.1 队列的应用
      • 3.2.2 管道的应用
  • 4.性能优化与选择
    • 4.1 协程的应用
    • 4.2 异步编程的应用
  • 5.继续探索:多线程与多进程的高级应用
    • 5.1 线程池的应用
    • 5.2 进程池的应用
    • 5.3 多线程与多进程的对比
  • 写在最后

写在开头

随着计算机硬件的发展和任务的日益复杂,如何更有效地利用计算机资源成为程序开发者面临的一项重要任务。Python 作为一门强大而灵活的编程语言,提供了多线程和多进程的支持,为并发编程打开了全新的大门。本文将深入介绍Python中多线程与多进程的使用,带您领略编织并发的魔法纹章之美。

1.理解并发编程的基本概念

在深入介绍多线程和多进程之前,让我们首先理解并发编程的基本概念。
并发编程是一种同时处理多个任务的编程模式,通过有效地管理多个任务的执行,以提高程序性能、响应速度和资源利用率。在现代计算机系统中,硬件趋势朝着多核心的方向发展,因此并发编程变得更加重要,能够更好地充分利用计算资源。

1.1 并发与并行的区别

在并发编程的讨论中,经常会提到并发与并行这两个概念,它们有一些微妙的区别:

  • 并发(Concurrency):指的是在同一时间间隔内处理多个任务,这些任务可能并不是同时执行的,而是通过快速切换的方式共享CPU时间。并发通常用于处理I/O密集型任务,如文件操作、网络请求等。

  • 并行(Parallelism):指的是同时执行多个任务,每个任务在不同的CPU核心上运行。并行通常用于处理CPU密集型任务,如大规模数据处理、科学计算等。

并发和并行并非是互斥的概念,实际上,并发通常是通过并行来实现的。在多核处理器上,可以通过并行执行来实现并发,从而更好地处理多个任务。

1.2 并发编程的目标

并发编程的主要目标是提高程序的性能、响应速度和资源利用率,同时保持程序的正确性。下面会列举并发编程的一些核心目标:

  • 提高性能:通过同时执行多个任务,充分利用计算资源,加速程序的执行。

  • 提高响应速度:在某些情况下,通过并发处理可以使程序更快地响应用户请求,提升用户体验。

  • 提高资源利用率:有效地利用计算资源,确保系统资源得到充分利用,避免资源浪费。

  • 保持程序正确性:并发编程引入了竞争条件、死锁、数据共享等问题,确保程序正确执行需要采取适当的同步和协调措施。

1.3 并发编程的挑战

并发编程面临一些挑战,主要包括以下几个方面:

  • 竞争条件(Race Condition):多个线程或进程同时访问共享资源,导致不确定的执行结果。

  • 死锁(Deadlock):两个或多个任务相互等待对方释放资源,导致程序无法继续执行。

  • 数据共享与同步:多个任务同时访问和修改共享数据,需要采取同步机制来保证数据的一致性。

  • 上下文切换开销:在多任务切换时,操作系统需要保存和恢复任务的上下文,引入了额外的开销。

1.4 并发编程的实现方式

在Python中,实现并发编程主要有以下几种方式:

  • 多线程(Threading):使用threading模块创建多个线程,通过共享内存进行通信。

  • 多进程(Multiprocessing):使用multiprocessing模块创建多个进程,每个进程有独立的内存空间,通过进程间通信(如队列、管道)进行数据传递。

  • 协程(Coroutine):使用asyncio模块实现协程,通过async/await关键字进行异步编程,提高I/O密集型任务的效率。

  • 异步编程(Asynchronous Programming):结合协程和事件循环,通过asyncio模块实现异步编程,提高程序的响应速度。

以下是多线程、多进程、协程和异步编程的对比分析:

特征多线程多进程协程异步编程
并行执行在同一进程中的多个线程并发执行。每个进程拥有独立的地址空间,可并行执行。在同一线程中的多个协程并发执行。在同一线程中的多个异步任务并发执行。
数据共享与通信共享同一进程的地址空间,共享数据更方便。需要通过进程间通信(IPC)来共享数据。在同一线程中共享数据,需要考虑同步机制。在异步编程中,通过事件循环进行任务切换,数据通信相对简化。
内存消耗比多进程更轻量,共享内存。每个进程有独立的内存空间,内存占用相对较高。比多线程更轻量,共享内存。比多线程更轻量,不需要为每个任务分配独立的内存。
创建与销毁成本创建线程较为轻量,成本相对较低。创建和销毁进程较为耗时,成本较高。创建和销毁协程较为轻量,成本相对较低。创建和销毁异步任务较为轻量,成本相对较低。
同步与锁多线程需要考虑竞争条件,使用锁进行同步。多进程之间相对独立,通常不需要使用锁。协程之间共享同一线程,需要考虑同步机制。异步编程中通过异步关键字async/await进行协程间同步。
故障影响范围一个线程的故障可能影响整个进程。一个进程的故障不会影响其他进程。协程运行在同一线程中,一个协程的错误可能影响其他协程。异步编程中一个任务的错误不会影响其他任务。
切换开销线程切换的开销相对较小。进程切换的开销相对较大。协程切换的开销较小。异步编程通过事件循环进行任务切换,开销相对较小。
适用场景适用于I/O密集型任务,共享数据的情况。适用于CPU密集型任务,需要独立内存空间的情况。适用于I/O密集型任务,高并发情况。适用于I/O密集型任务,需要高并发的情况。

1.5 并发编程的选择

选择合适的并发编程方式取决于任务的性质、硬件条件以及编程者的经验。在选择时需要权衡多线程、多进程、协程和异步编程等不同方式的优劣,以最大程度地满足程序的性能和响应要求。

2.魔法的交织:使用threadingmultiprocessing模块

Python 提供了两个主要模块来支持并发编程:threading 用于多线程,multiprocessing 用于多进程。通过这两个模块,我们可以在同一程序中同时执行多个任务,以提高程序的性能和响应速度。

2.1 编织多线程的咒语

在 Python 中,多线程通过 threading 模块实现。让我们通过一个例子来演示如何使用咒语编织多线程的力量。

import threading
import timedef print_numbers():for i in range(5):time.sleep(1)print(f"Thread 1: {i}")def print_letters():for letter in 'ABCDE':time.sleep(1)print(f"Thread 2: {letter}")# 创建两个线程
thread1 = threading.Thread(target=print_numbers)
thread2 = threading.Thread(target=print_letters)# 启动线程
thread1.start()
thread2.start()# 等待线程结束
thread1.join()
thread2.join()print("Main thread exiting.")

2.2 简单的并发编程示例

除了多线程,multiprocessing 模块使得我们能够创建和管理多个进程。以下是一个简单的多进程示例,演示了如何使用魔法咒语编织多进程的奇迹。

import multiprocessing
import timedef print_numbers():for i in range(5):time.sleep(1)print(f"Process 1: {i}")def print_letters():for letter in 'ABCDE':time.sleep(1)print(f"Process 2: {letter}")# 创建两个进程
process1 = multiprocessing.Process(target=print_numbers)
process2 = multiprocessing.Process(target=print_letters)# 启动进程
process1.start()
process2.start()# 等待进程结束
process1.join()
process2.join()print("Main process exiting.")

3.并发的魔法表演

在并发编程中,特别是在多线程和多进程的场景中,确保共享数据的正确性是至关重要的。同时,进程间通信也是多进程编程的一项挑战。本节将详细展开并介绍共享数据与同步、进程间通信两个方面的内容。

3.1 共享数据与同步

3.1.1 共享数据

共享数据是指多个线程或进程同时访问和修改的数据,如下例所示:

import threading# 共享数据
shared_data = 0def increment_shared_data():global shared_datafor _ in range(1000000):shared_data += 1# 创建两个线程
thread1 = threading.Thread(target=increment_shared_data)
thread2 = threading.Thread(target=increment_shared_data)# 启动线程
thread1.start()
thread2.start()# 等待线程结束
thread1.join()
thread2.join()print(f"Final shared data value: {shared_data}")

3.1.2 锁的应用

为了确保对共享数据的安全访问,我们可以使用锁(Lock)进行同步。锁可以在任一时刻只允许一个线程访问共享资源,从而避免竞争条件。

import threading# 共享数据
shared_data = 0
# 创建锁
lock = threading.Lock()def increment_shared_data():global shared_datafor _ in range(1000000):# 获取锁with lock:shared_data += 1# 创建两个线程
thread1 = threading.Thread(target=increment_shared_data)
thread2 = threading.Thread(target=increment_shared_data)# 启动线程
thread1.start()
thread2.start()# 等待线程结束
thread1.join()
thread2.join()print(f"Final shared data value: {shared_data}")

3.2 进程间通信

在多进程编程中,不同进程之间需要进行通信,Python的multiprocessing模块提供了多种方式来实现进程间通信,其中包括队列(Queue)、管道(Pipe)等。

3.2.1 队列的应用

队列是多进程中常用的通信方式,它提供了先进先出(FIFO)的数据结构,通过put()get()方法实现进程之间的数据传递。

import multiprocessingdef producer(queue):for i in range(5):queue.put(f"Message {i}")def consumer(queue):while True:message = queue.get()if message == "END":breakprint(f"Received: {message}")if __name__ == "__main__":# 创建队列message_queue = multiprocessing.Queue()# 创建生产者和消费者进程producer_process = multiprocessing.Process(target=producer, args=(message_queue,))consumer_process = multiprocessing.Process(target=consumer, args=(message_queue,))# 启动进程producer_process.start()consumer_process.start()# 等待生产者结束producer_process.join()# 向队列发送结束信号message_queue.put("END")# 等待消费者结束consumer_process.join()

3.2.2 管道的应用

管道是另一种进程间通信的方式,通过Pipe()方法创建,返回两个连接的管道端口,可以分别用于双向通信。

import multiprocessingdef sender(pipe):for message in ["Message 1", "Message 2", "Message 3"]:pipe.send(message)def receiver(pipe):while True:message = pipe.recv()if message == "END":breakprint(f"Received: {message}")if __name__ == "__main__":# 创建管道parent_pipe, child_pipe = multiprocessing.Pipe()# 创建发送者和接收者进程sender_process = multiprocessing.Process(target=sender, args=(child_pipe,))receiver_process = multiprocessing.Process(target=receiver, args=(parent_pipe,))# 启动进程sender_process.start()receiver_process.start()# 等待发送者结束sender_process.join()# 向管道发送结束信号parent_pipe.send("END")# 等待接收者结束receiver_process.join()

4.性能优化与选择

在实际应用中,选择多线程还是多进程,取决于任务的性质和硬件条件。多线程适用于I/O密集型任务,而多进程适用于CPU密集型任务。在性能优化方面,还可以考虑使用协程、异步编程等方式,以更充分地利用计算资源。

4.1 协程的应用

协程是一种轻量级的并发编程方式,通过asyncio模块提供支持。下面是一个简单的协程示例:

import asyncioasync def coro_example():print("Start Coroutine")await asyncio.sleep(2)print("End Coroutine")# 使用事件循环运行协程
loop = asyncio.get_event_loop()
loop.run_until_complete(coro_example())

4.2 异步编程的应用

异步编程通过async/await关键字实现,通过非阻塞的方式处理并发任务,提高程序的执行效率。以下是一个简单的异步编程示例:

import asyncioasync def async_example():print("Start Async Task 1")await asyncio.sleep(2)print("End Async Task 1")async def async_example2():print("Start Async Task 2")await asyncio.sleep(1)print("End Async Task 2")# 使用事件循环运行异步任务
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(async_example(), async_example2()))

5.继续探索:多线程与多进程的高级应用

在深入了解共享数据与同步、进程间通信之后,我们将进一步探索多线程和多进程的一些高级应用场景。

5.1 线程池的应用

线程池是一种用于管理和重用线程的机制,通过concurrent.futures模块提供支持。它可以有效地管理线程的数量,减少线程的创建和销毁开销。

from concurrent.futures import ThreadPoolExecutor
import timedef task(message):time.sleep(1)return f"Task: {message}"# 创建线程池
with ThreadPoolExecutor(max_workers=3) as executor:# 提交任务futures = [executor.submit(task, i) for i in range(5)]# 获取结果for future in concurrent.futures.as_completed(futures):result = future.result()print(result)

5.2 进程池的应用

进程池与线程池类似,用于管理和重用进程。通过concurrent.futures模块的ProcessPoolExecutor来创建进程池。

from concurrent.futures import ProcessPoolExecutor
import timedef task(message):time.sleep(1)return f"Task: {message}"# 创建进程池
with ProcessPoolExecutor(max_workers=3) as executor:# 提交任务futures = [executor.submit(task, i) for i in range(5)]# 获取结果for future in concurrent.futures.as_completed(futures):result = future.result()print(result)

5.3 多线程与多进程的对比

多线程和多进程各有优劣,取决于具体场景和任务性质。在一些情况下,两者的组合也可能是一种有效的选择,通过ThreadPoolExecutorProcessPoolExecutor可以更方便地管理线程池和进程池。

from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import timedef task(message):time.sleep(1)return f"Task: {message}"# 创建线程池和进程池
with ThreadPoolExecutor(max_workers=3) as thread_executor, ProcessPoolExecutor(max_workers=3) as process_executor:# 提交任务到线程池thread_futures = [thread_executor.submit(task, i) for i in range(5)]# 提交任务到进程池process_futures = [process_executor.submit(task, i) for i in range(5)]# 获取线程池结果for future in concurrent.futures.as_completed(thread_futures):result = future.result()print(f"Thread Result: {result}")# 获取进程池结果for future in concurrent.futures.as_completed(process_futures):result = future.result()print(f"Process Result: {result}")

写在最后

共享数据与同步、进程间通信等是并发编程中的重要议题,特别是在多线程和多进程的场景中。在选择并发编程方式时,根据任务性质与硬件条件选择多线程或多进程,并结合共享数据与同步、进程间通信的方式,将会更好地编织出并发的魔法纹章。最终,性能优化与选择则是实现高效并发编程的关键一环,考虑使用协程、异步编程等方式,以更充分地发挥计算资源的潜力。希望本文内容能够为您在并发编程的奇妙旅程中提供有益的指导。

这篇关于Python多线程与多进程:编织并发的魔法纹章的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

使用Python将JSON,XML和YAML数据写入Excel文件

《使用Python将JSON,XML和YAML数据写入Excel文件》JSON、XML和YAML作为主流结构化数据格式,因其层次化表达能力和跨平台兼容性,已成为系统间数据交换的通用载体,本文将介绍如何... 目录如何使用python写入数据到Excel工作表用Python导入jsON数据到Excel工作表用

Python基础语法中defaultdict的使用小结

《Python基础语法中defaultdict的使用小结》Python的defaultdict是collections模块中提供的一种特殊的字典类型,它与普通的字典(dict)有着相似的功能,本文主要... 目录示例1示例2python的defaultdict是collections模块中提供的一种特殊的字

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析

基于Python实现高效PPT转图片工具

《基于Python实现高效PPT转图片工具》在日常工作中,PPT是我们常用的演示工具,但有时候我们需要将PPT的内容提取为图片格式以便于展示或保存,所以本文将用Python实现PPT转PNG工具,希望... 目录1. 概述2. 功能使用2.1 安装依赖2.2 使用步骤2.3 代码实现2.4 GUI界面3.效

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

python连接本地SQL server详细图文教程

《python连接本地SQLserver详细图文教程》在数据分析领域,经常需要从数据库中获取数据进行分析和处理,下面:本文主要介绍python连接本地SQLserver的相关资料,文中通过代码... 目录一.设置本地账号1.新建用户2.开启双重验证3,开启TCP/IP本地服务二js.python连接实例1.

基于Python和MoviePy实现照片管理和视频合成工具

《基于Python和MoviePy实现照片管理和视频合成工具》在这篇博客中,我们将详细剖析一个基于Python的图形界面应用程序,该程序使用wxPython构建用户界面,并结合MoviePy、Pill... 目录引言项目概述代码结构分析1. 导入和依赖2. 主类:PhotoManager初始化方法:__in

Python从零打造高安全密码管理器

《Python从零打造高安全密码管理器》在数字化时代,每人平均需要管理近百个账号密码,本文将带大家深入剖析一个基于Python的高安全性密码管理器实现方案,感兴趣的小伙伴可以参考一下... 目录一、前言:为什么我们需要专属密码管理器二、系统架构设计2.1 安全加密体系2.2 密码强度策略三、核心功能实现详解

Python Faker库基本用法详解

《PythonFaker库基本用法详解》Faker是一个非常强大的库,适用于生成各种类型的伪随机数据,可以帮助开发者在测试、数据生成、或其他需要随机数据的场景中提高效率,本文给大家介绍PythonF... 目录安装基本用法主要功能示例代码语言和地区生成多条假数据自定义字段小结Faker 是一个 python

Python实现AVIF图片与其他图片格式间的批量转换

《Python实现AVIF图片与其他图片格式间的批量转换》这篇文章主要为大家详细介绍了如何使用Pillow库实现AVIF与其他格式的相互转换,即将AVIF转换为常见的格式,比如JPG或PNG,需要的小... 目录环境配置1.将单个 AVIF 图片转换为 JPG 和 PNG2.批量转换目录下所有 AVIF 图