python:并发编程(一)

前言

本文将和大家一起探讨并发编程,而不限于python语言。后续文章,我们将一起学习并发编程的相关模块。为什么专门写并发编程的文章呢?一个重要原因是有趣,而且实用。写一些简单的脚本可能很少用到并发编程,但是要提高这些脚本的工作效率,并发编程是个不错的选择。

本文为python并发编程的开篇之作,下一篇文章地址如下:

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

一、并发编程相关技术及相关概念

说到并发编程,则离不开进程、线程、协程等相关概念,因为他们可以用于实现并发编程。进程是操作系统对程序运行的基本单位,一个进程可以包含多个线程。线程是操作系统能够进行运算调度的最小单位,多个线程可以在同一个进程中并发执行。协程是一种轻量级的线程,由程序控制任务的切换和执行顺序,适用于I/O密集型任务。

如果不理解上述表述,那么我们将以剥洋葱的方式进行学习。

1、进程

进程是正在运行的程序的实例。一个进程可以包含多个线程,每个线程执行不同的任务。进程之间相互独立,拥有独立的地址空间和系统资源。进程间通信需要通过特定的机制,如管道、共享内存、消息队列等。

每个进程都有自己独立的解释器和GIL,因此可以充分利用多核处理器进行并行计算,适用于CPU密集型任务。

2、线程

线程是操作系统能够进行运算调度的最小单位。在一个进程中可以有多个线程同时执行,它们共享进程的资源,但每个线程都有自己的执行路径和栈空间。线程是轻量级的,并且可以通过多线程编程实现并发任务的执行。

通过创建多个线程,可以在同一时间执行多个任务,从而提高程序的并发性。然而,由于GIL(全局解释器锁)的存在,Python中的多线程并不适用于CPU密集型任务,但适用于I/O密集型任务。注意,这里专门点名了Python,也就是说编程语言的多线程是否适用于 CPU 密集型任务,主要取决于编程语言的线程模型和执行环境。

3、协程

协程是一种轻量级的线程,也被称为用户级线程。协程通过协作式的方式进行任务切换,不需要操作系统进行调度。协程的切换是在代码中显式地指定的,可以更灵活地控制任务的切换和执行顺序。协程适用于I/O密集型任务,能够提高程序的效率。

协程是一种轻量级的线程,通过创建多个“轻量级的线程”,可以在同一个线程中实现并发。

4、I/O密集型任务

I/O 密集型任务是指任务主要涉及输入和输出操作,而不是大量的计算操作。在 I/O 密集型任务中,大部分时间都花在等待输入/输出完成或等待外部资源的响应上,而不是进行复杂的计算。典型的 I/O 密集型任务包括文件操作、网络通信、数据库访问等。

5、CPU 密集型任务

CPU 密集型任务是指任务主要涉及大量的计算操作,而不涉及太多的 I/O 操作。在 CPU 密集型任务中,大部分时间都花在执行复杂的计算逻辑上,而不是等待外部资源的响应。

6、程序实例

程序实例是指程序在计算机系统中的一个具体实例。在计算机中,程序通常作为一系列指令的集合,用于描述要执行的操作。当程序被加载到内存并开始执行时,就会创建一个程序实例。

进程可以看作是正在运行的程序的实例,它具有独立的内存空间和资源,并能够被操作系统进行调度和管理。通过进程,程序可以在计算机系统中得以运行并完成各种任务。

7、运算调度的最小单位

运算调度的最小单位通常是指操作系统能够对其进行调度和分配处理器时间的最小执行单元。在大多数操作系统中,线程被认为是最小的运算调度单位。那为什么不是协程呢?在特定场景下也可以是协程(“轻量级的线程”),但其调度和管理通常由程序显式控制,而非由操作系统进行调度。

线程作为操作系统调度的最小单位,能够更细粒度地进行任务切换和资源共享,以提高系统的并发性能和响应性。

8、解释器

解释器是指在操作系统中执行代码的程序。对于不同的编程语言,存在不同的解释器。

在Python中,解释器是用于执行Python代码的程序。Python解释器负责解析和执行Python脚本或交互式命令。常见的Python解释器包括 CPython、Jython、IronPython、PyPy 等。其中,CPython 是官方的、最常用的Python解释器,它是用C语言实现的,并且与Python语言高度兼容。

解释器的主要功能是将源代码转换为可执行的机器码或字节码,并执行相应的指令。它负责管理内存、处理程序运行时的异常、进行资源分配和回收等任务。解释器还提供了调试、性能优化、编译等功能,以支持代码的开发和执行。

9、GIL

GIL(全局解释器锁)是Python解释器中的一个机制,用于控制多线程并发执行时对共享资源(主要是内存管理)的访问。GIL的存在是由于CPython解释器的设计选择。

在CPython解释器中,GIL保证在任意时刻只有一个线程执行Python字节码。这意味着多个线程不能并行执行Python字节码,而是通过在解释器级别进行协作调度,每个线程在获得GIL后才能执行一段时间。GIL的存在导致了Python中的多线程不能充分利用多核处理器的并行计算能力。这就是Python中的多线程并不适用于CPU密集型任务的原因。

二、并发编程方式的选择

通过前面的学习,我们已经简单的知道,对于python来说,如果是I/O密集型任务,需要使用多线程或多协程;如果是CPU 密集型任务,需要使用多进程。原因也简单的说明了一下,但有必要深入了解一下。

1、对于 I/O 密集型任务,多线程或多协程编程的优势

并发性:通过使用多线程,可以同时处理多个 I/O 操作,提高系统的并发处理能力,缩短任务的响应时间。

阻塞与非阻塞 I/O:多线程可以实现阻塞式或非阻塞式的 I/O 操作。在阻塞式 I/O 中,一个线程会在执行 I/O 操作时被阻塞,直到 I/O 操作完成;而在非阻塞式 I/O 中,一个线程可以在等待 I/O 操作完成的同时继续执行其他任务。

异步编程:使用多线程,可以实现异步编程模型,其中一个线程可以在执行 I/O 操作时切换到其他任务,待 I/O 操作完成后再切换回来继续执行。

资源共享:多线程可以共享同一个进程的资源,例如共享内存或数据库连接等,使得多个线程可以同时对资源进行读写操作,提高系统的资源利用率。

2、对于CPU密集型任务,多进程编程的优势

利用多核处理器:多进程可以充分利用多核处理器的并行计算能力,每个进程运行在独立的CPU核上,可以同时进行计算,从而提高整体的计算速度和性能。

克服GIL限制:在CPython解释器中,由于GIL的存在,多线程并不能实现真正的并行计算。而多进程编程可以通过创建多个独立的进程来克服GIL的限制,每个进程拥有自己独立的解释器和GIL,可以并行执行计算任务。

高稳定性和隔离性:每个进程都有自己独立的内存空间,彼此之间相互隔离,因此一个进程的错误或异常不会影响其他进程的稳定性。如果某个进程发生崩溃或异常退出,其他进程仍然可以继续执行。

更好的资源管理:多进程编程可以有效地管理系统资源,每个进程拥有独立的系统资源分配,如内存、文件句柄等,可以更好地控制和管理这些资源,避免资源竞争和冲突。

并行任务调度:多进程可以将任务分解为多个子任务,并在不同的进程中并行执行这些子任务,从而实现更高效的任务调度和执行。每个进程可以独立地执行特定的计算任务,提高整体的计算效率。

3、对于 CPU 密集型任务,多线程编程的效果可能会受到限制

全局解释器锁(GIL):在某些编程语言(如 Python)中,存在全局解释器锁(GIL),它限制了同一进程中的多个线程同时执行 Python 字节码的能力。这意味着在 CPU 密集型任务中,即使使用了多线程,实际上只有一个线程能够执行计算操作,其他线程需要等待 GIL 的释放才能执行。

线程切换开销:在多线程环境下,线程的切换也会带来一定的开销。如果任务主要是计算操作,并且没有太多的并发需求,那么线程的切换开销可能会超过实际的计算时间,导致性能下降。

所以,对于 CPU 密集型任务,python就不用考虑多线程了,其他语言是可以考虑的。

猜你喜欢

转载自blog.csdn.net/weixin_43431593/article/details/131188418
今日推荐