详细介绍一下Python多线程技术

360影视 2025-01-17 01:54 2

摘要:Python首先来讲作为一门目前来讲使用比较广泛的编程语言以其简单高效的语法受到了大多数开发者的喜爱。但是在某些应用场景中,我们可能需要多个任务来同时去执行操作,这个时候,就涉及到并发操作的问题。一般情况下,我们解决并发执行操作的主要方式就是通过多线程来实现,

Python首先来讲作为一门目前来讲使用比较广泛的编程语言以其简单高效的语法受到了大多数开发者的喜爱。但是在某些应用场景中,我们可能需要多个任务来同时去执行操作,这个时候,就涉及到并发操作的问题。一般情况下,我们解决并发执行操作的主要方式就是通过多线程来实现,尤其是在一些I/O密集型的任务中,多线程可以帮助我们更快更好的处理问题。当然在Python中也不例外,下面我们就从Python中多线程的概念、如何实现多线程等方面来详细介绍一下Python中的多线程技术。

  相比大家对于线程应该是不陌生的,它是计算机程序执行的最小单位。如果将一个进程看做是一个应用的话,那么在这个进程中就可以包含多个线程,这些线程共享通过共享进程的资源来实现多任务操作。所谓的多任务操作就是指在同一时间内可以同时执行多个任务处理,并且每个任务处理相互之间不干扰。这样做的目的就是为了更好的提升程序执行的效率,尤其是在一些大批量处理任务的时候,我们可以将批量的任务拆分到不同的线程中执行,以此来提升程序执行的效率。

  其实在上面的内容中我们也提到了,使用多线程主要的优势就在于提升程序的执行效率,尤其是在一些I/O密集型的操作中,可能很多人不知道什么是I/O密集型的操作,这里我们简单介绍一下,I/O密集型的任务包括对于文件的操作、网络请求操作、数据库查询操作等等。这些任务有一个共同的特点就是需要等待外部资源的响应,这种操作一般考验的并不是CPU的计算能力,这种情况下,通过多线程技术可以让一个线程在等待外部I/O响应,而其他线程可以继续执行其他的操作,从而避免造成CPU资源的浪费。

  其实在很多开发语言中都提供了多线程的支持,在Python中也不例外,我们可以通过threading 模块来实现Python中的多线程技术。

  threading 模块为 Python 提供了线程创建、管理和同步等功能。为了能够更好的使用这个模块,我们首先来了解一下相关的概念。

创建线程

  在Python中,我们可以通过继承threading.Thread 类或者通过直接调用 threading.Thread 的构造函数来创建线程,如下所示,通过继承Thread类来创建线程。

import threadingimport time# 继承Thread类class MyThread(threading.Thread):def run(self):for i in range(5):time.sleep(1)print(f"Thread {self.name} is running...")# 创建线程thread1 = MyThreadthread2 = MyThread# 启动线程thread1.startthread2.start# 等待线程结束thread1.jointhread2.joinprint("Main thread exits.")

  当然除了上面这种方式,我们还可以通过直接调用 threading.Thread 的构造函数的方式来创建线程,如下所示。

import threadingimport timedef task:for i in range(5):time.sleep(1)print(f"Thread {threading.current_thread.name} is running...")# 创建线程thread1 = threading.Thread(target=task)thread2 = threading.Thread(target=task)# 启动线程thread1.startthread2.start# 等待线程结束thread1.jointhread2.joinprint("Main thread exits.")

  在上面的例子中,演示了如何创建多个线程并且介绍了通过start方法来启动线程,然后通过join方法来等待线程执行完毕然后退出主线程的操作。

线程同步

  其实线程同步的概念在之前的分享中我们介绍过,在多线程共享某些资源的时候,可能会出现多个线程竞争共享资源的情况,这样就会导致各种的线程安全问题,为了解决这个情况,在Python中提供了几种线程同步机制,来保证多线程操作的安全性,如下所示。

  我们可以通过Lock操作来实现线程同步。

import threadinglock = threading.Lockdef task:with lock: # 使用 lock 进行同步for i in range(5):print(f"Thread {threading.current_thread.name} is running...")# 创建线程thread1 = threading.Thread(target=task)thread2 = threading.Thread(target=task)# 启动线程thread1.startthread2.start# 等待线程结束thread1.jointhread2.joinprint("Main thread exits.")

  在这个例子中,我们通过Lock对共享的数据资源进行了保护,这样就可以保证在同一时刻只会有一个线程能够访问被lock 保护的代码块,从而避免了数据竞争问题,当然除了这种方式之外,还有RLock 和 Semaphore 等方式都可以实现线程同步机制,但是原理都是一样的。只不过实现方式不同罢了。

线程的状态和管理

  在之前我们介绍Java相关的概念的时候,就提到过线程的生命周期以及状态转换,例如线程的状态可以包括 新建(New)就绪(Runnable)阻塞(Blocked) 和 **终止(Terminated)**等等,我们可以通过Thread类中提供的一些方法来对线程状态进行管理,如下所示。

import threadingimport timedef task:time.sleep(2)print(f"Thread {threading.current_thread.name} finished.")# 创建线程thread = threading.Thread(target=task, name="MyThread")# 启动线程thread.start# 检查线程状态print(f"Is thread alive? {thread.is_alive}")thread.joinprint(f"Is thread alive? {thread.is_alive}")print("Main thread exits.")

  GIL全局解释器锁是Python解释器中一个非常重要的机制,它能够确保在同一个时刻只能有一个线程可以执行Python的字节码,这也就是说在多CPU的机器上,Python可能也无法充分的利用多核心计算的能力。换句话说,Python的多线程技术适用于I/O密集型的任务,但是对于CPU密集型的任务,例如数值计算、图像处理等并不能显著的提高其性能,反而会因为线程之间的上下文切换所带来性能的下降。

  很多人就很好奇了,那不是还有很多的CPU密集型的任务是通过Python语言开发的么?其实这种GIL的限制并不是没有办法解决的,我们可以通过如下的方式来解决这些限制。

多进程:对于 CPU 密集型任务,可以使用 multiprocessing 模块,它允许创建多个进程,每个进程都有自己的 Python 解释器和 GIL,从而避免 GIL 的限制,对于多进程技术,有兴趣的读者可以自己了解这里不做过多介绍。使用 C 扩展:可以通过使用像 numpy、scipy 等 C 扩展来进行计算,这些扩展通过直接调用 C 代码,能够绕过 GIL。当然在Python中使用的很多计算类型的库都是依托于C语言进行的扩展,基于C语言的代码就能够打破GIL的限制。异步编程:对于一些I/O密集型的操作任务,Python提供了asyncio库来提供更加高效的方式来实现并发执行操作,这样就可以有效的避免线程切换带来的开销。

  Python 的多线程技术在 I/O 密集型任务中可以帮助开发者充分利用 CPU 等待 I/O 操作的空闲时间。但是,由于 GIL 的存在,Python 多线程在 CPU 密集型任务中的效果并不显著。但是在实际开发中,我们可以通过调整GIL和线程同步机制选择合适的技术来发挥出Python多线程的优势。通过掌握Python中的多线程技术,我们可以轻松地在多个任务之间实现并发执行,提高程序的效率。

来源:从程序员到架构师

相关推荐