当前位置: 首页 > article >正文

【30天玩转python】多线程与多进程编程

多线程与多进程编程

在 Python 中,多线程和多进程是实现并发编程的重要手段。多线程适用于 I/O 密集型任务,而多进程则更适合 CPU 密集型任务。通过合理使用多线程和多进程,可以有效提升程序的执行效率和性能。


1. 多线程编程

1.1 线程的概念

线程是操作系统能够进行调度的最小单位。每个线程包含了一个独立的执行路径,多个线程可以在同一个进程内共享内存和资源。Python 中的多线程编程主要使用 threading 模块。

1.2 创建多线程

在 Python 中,可以通过继承 threading.Thread 类来创建新的线程,也可以使用 threading 模块直接创建线程对象。

示例:创建并启动线程

import threading
import time

def print_numbers():
    for i in range(1, 6):
        time.sleep(1)
        print(f"Number: {i}")

# 创建线程
t = threading.Thread(target=print_numbers)

# 启动线程
t.start()

# 等待线程执行完毕
t.join()

print("线程执行完毕")

在这个例子中,print_numbers 函数会在新线程中运行,主线程继续等待直到新线程执行完毕后,才打印“线程执行完毕”。

1.3 使用继承创建线程
class MyThread(threading.Thread):
    def run(self):
        for i in range(1, 6):
            time.sleep(1)
            print(f"MyThread Number: {i}")

# 创建并启动线程
t = MyThread()
t.start()
t.join()
1.4 线程锁(Lock)

在多线程环境下,由于多个线程共享内存和资源,可能会出现竞争条件(race condition),从而导致数据不一致或错误。为了解决这个问题,可以使用线程锁(threading.Lock)来同步线程。

示例:使用线程锁

lock = threading.Lock()
counter = 0

def increment_counter():
    global counter
    for _ in range(1000000):
        with lock:
            counter += 1

t1 = threading.Thread(target=increment_counter)
t2 = threading.Thread(target=increment_counter)

t1.start()
t2.start()

t1.join()
t2.join()

print(f"Final counter: {counter}")

在这个例子中,使用 with lock 确保每个线程在修改 counter 时,不会同时被其他线程修改,从而避免竞争条件。


2. 多进程编程

2.1 进程的概念

进程是资源分配的最小单位,每个进程都有自己独立的内存空间。多进程编程可以通过创建多个独立的进程来实现并发。Python 中的多进程编程主要使用 multiprocessing 模块。

2.2 创建多进程

multiprocessing.Process 类提供了与 threading.Thread 类类似的 API,用于创建新进程。

示例:创建并启动进程

import multiprocessing
import time

def print_numbers():
    for i in range(1, 6):
        time.sleep(1)
        print(f"Process Number: {i}")

# 创建进程
p = multiprocessing.Process(target=print_numbers)

# 启动进程
p.start()

# 等待进程执行完毕
p.join()

print("进程执行完毕")

与多线程不同的是,每个进程都有独立的内存空间,因此需要通过进程间通信(IPC)机制来共享数据。

2.3 使用继承创建进程
class MyProcess(multiprocessing.Process):
    def run(self):
        for i in range(1, 6):
            time.sleep(1)
            print(f"MyProcess Number: {i}")

# 创建并启动进程
p = MyProcess()
p.start()
p.join()
2.4 进程间通信

由于进程之间不共享内存,因此需要通过管道(Pipe)或队列(Queue)来实现进程间通信。

示例:使用队列进行进程间通信

import multiprocessing

def worker(q):
    q.put([42, None, 'hello'])

if __name__ == "__main__":
    q = multiprocessing.Queue()
    p = multiprocessing.Process(target=worker, args=(q,))
    p.start()
    print(q.get())  # 从队列中获取数据
    p.join()

在这个例子中,主进程通过队列与子进程通信,子进程将数据放入队列,主进程从队列中读取数据。


3. 多线程 vs 多进程

特性多线程多进程
内存共享线程间共享内存进程间不共享内存
创建开销线程创建开销较小进程创建开销较大
适用场景I/O 密集型任务(如文件读写、网络)CPU 密集型任务(如复杂计算)
数据隔离线程间数据不隔离,需同步进程间数据完全隔离,需通信
并行性受限于 Python 的 GIL(全局解释器锁)进程可以真正并行执行
故障隔离一个线程崩溃可能影响整个进程进程崩溃不会影响其他进程

4. Global Interpreter Lock (GIL)

Python 中的全局解释器锁(GIL)限制了多个线程同时执行 Python 字节码。尽管可以使用多线程,但在 CPU 密集型任务中,GIL 会导致 Python 程序无法真正并行执行。因此,CPU 密集型任务通常使用多进程来绕过 GIL 的限制。


5. 线程池与进程池

如果需要管理大量线程或进程,使用线程池或进程池会更高效。Python 提供了 concurrent.futures.ThreadPoolExecutorconcurrent.futures.ProcessPoolExecutor 来简化多线程和多进程的管理。

5.1 使用线程池
from concurrent.futures import ThreadPoolExecutor

def task(n):
    return n * 2

with ThreadPoolExecutor(max_workers=3) as executor:
    futures = [executor.submit(task, i) for i in range(5)]
    for future in futures:
        print(future.result())
5.2 使用进程池
from concurrent.futures import ProcessPoolExecutor

def task(n):
    return n * 2

with ProcessPoolExecutor(max_workers=3) as executor:
    futures = [executor.submit(task, i) for i in range(5)]
    for future in futures:
        print(future.result())

6. 小结

  • 多线程:适用于 I/O 密集型任务,线程间共享内存,但需使用锁同步以避免竞争条件。由于 GIL 的存在,CPU 密集型任务中多线程效果有限。
  • 多进程:适用于 CPU 密集型任务,进程之间数据隔离,真正实现并行执行。适合处理大规模计算任务或需要并行处理的任务。

通过合理选择多线程或多进程,可以显著提高程序的执行效率。在开发过程中,需要根据具体场景选择合适的并发编程模型。


http://www.kler.cn/a/307527.html

相关文章:

  • HTTP 客户端怎么向 Spring Cloud Sleuth 传输跟踪 ID
  • ubuntu20.04 解决Pytorch默认安装CPU版本的问题
  • C# 模拟浏览器自操作(自动化办公)
  • quartz
  • Python提取PDF和DOCX中的文本、图片和表格
  • JVM详解:JVM的系统架构
  • 怎么把网站设置成HTTPS访问?
  • html+css网页制作 旅游 厦门旅游网3个页面
  • golang中连接达梦数据库使用域名来代替IP时会出现解析问题
  • c++ #include <cmath>介绍
  • TON智能合约stdlib_ext库:扩展功能一览
  • 一,掌心里的智慧:我的 TinyML 学习之旅
  • 类似mac dock的tab切换组件
  • 小琳AI课堂:LLaMA 3.1 开源大模型的全新里程碑
  • k8s的NodeIP、PodIP、ClusterIP、ExternalIP
  • 在 Java 中使用 bean 有什么好处
  • 通用四期ARM架构银河麒麟桌面操作系统V10【安装、配置FTP服务端】
  • Redis基础数据结构之 quicklist 和 listpack 源码解读
  • 棉花叶片病害检测数据集
  • Linux memcg lru lock提升锁性能
  • OpenSNN推文:神经网络(Neural Network)相关论文最新推荐(九月份)(二)
  • ElasticSearch数据类型和分词器
  • 通过防火墙分段增强网络安全
  • 基于SpringBoot的影城管理系统
  • 【Motion Forecasting】【摘要阅读】BANet: Motion Forecasting with Boundary Aware Network
  • Python中的单例模式:从入门到精通