深入理解 Python 线程:多线程编程的实践与技巧

在 Python 编程中,线程是一个强大的工具,它允许我们在程序中同时执行多个任务,从而提高程序的效率和响应性。本文将深入探讨 Python 线程的概念、使用方法以及在实际应用中需要注意的问题。

一、什么是线程

线程是进程中的一个执行单元,它是操作系统能够进行运算调度的最小单位。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间、文件描述符等。与进程不同,线程的创建和销毁开销较小,因此在处理 I/O 密集型任务时,多线程可以显著提高程序的执行效率。

二、Python 中的线程模块

Python 提供了两个主要的模块来处理线程:thread(低级模块)和threading(高级模块)。threading模块更加面向对象,功能更强大,使用起来也更加方便,因此在实际开发中,我们通常使用threading模块。

2.1 创建线程

要创建一个线程,我们可以继承threading.Thread类,并重写它的run方法。以下是一个简单的示例:

import threading


class MyThread(threading.Thread):
    def run(self):
        print(f"线程 {self.name} 开始执行")
        for i in range(5):
            print(f"线程 {self.name}: {i}")
        print(f"线程 {self.name} 执行结束")


# 创建线程实例
thread1 = MyThread()
thread2 = MyThread()

# 启动线程
thread1.start()
thread2.start()

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


# 创建线程, 也可以不用继承
# thread1 = threading.Thread(target = increment)

在这个示例中,我们定义了一个MyThread类,它继承自threading.Thread。在run方法中,我们定义了线程要执行的任务。然后,我们创建了两个MyThread实例,并使用start方法启动线程。最后,使用join方法等待线程执行完毕。

2.2 传递参数

有时候,我们需要向线程传递参数。可以在创建线程时通过构造函数传递参数。

import threading


class MyThread(threading.Thread):
    def __init__(self, num):
        super().__init__()
        self.num = num

    def run(self):
        print(f"线程 {self.name} 开始执行")
        for i in range(self.num):
            print(f"线程 {self.name}: {i}")
        print(f"线程 {self.name} 执行结束")


# 创建线程实例并传递参数
thread1 = MyThread(3)
thread2 = MyThread(5)

# 启动线程
thread1.start()
thread2.start()

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

在这个示例中,我们在MyThread类的构造函数中添加了一个参数num,并在run方法中使用这个参数。

三、线程同步

当多个线程同时访问共享资源时,可能会导致数据不一致或其他问题。为了避免这些问题,我们需要使用线程同步机制。

3.1 锁(Lock)

锁是一种最基本的线程同步机制。它可以确保在同一时间只有一个线程可以访问共享资源。

import threading

# 共享资源
counter = 0
lock = threading.Lock()


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


# 创建线程
thread1 = threading.Thread(target = increment)
thread2 = threading.Thread(target = increment)

# 启动线程
thread1.start()
thread2.start()

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

print(f"最终计数器的值: {counter}")

在这个示例中,我们使用Lock类创建了一个锁对象。在increment函数中,我们使用with lock语句来获取锁,确保在同一时间只有一个线程可以访问counter变量。

3.2 信号量(Semaphore)

信号量是一种更高级的锁机制,它允许同时有多个线程访问共享资源。

import threading

# 创建信号量,允许同时有2个线程访问
semaphore = threading.Semaphore(2)


def worker():
    semaphore.acquire()
    print(f"线程 {threading.current_thread().name} 获得信号量")
    # 模拟一些工作
    import time
    time.sleep(2)
    print(f"线程 {threading.current_thread().name} 释放信号量")
    semaphore.release()


# 创建多个线程
threads = []
for i in range(5):
    thread = threading.Thread(target = worker)
    threads.append(thread)
    thread.start()

# 等待所有线程执行完毕
for thread in threads:
    thread.join()

在这个示例中,我们创建了一个信号量对象,允许同时有 2 个线程访问共享资源。每个线程在访问共享资源之前,需要先调用acquire方法获取信号量,在访问完毕后,调用release方法释放信号量。

四、线程池

在实际应用中,频繁地创建和销毁线程会带来一定的开销。为了减少这种开销,我们可以使用线程池。线程池是一个预先创建好的线程集合,这些线程可以被重复使用来执行不同的任务。

Python 的concurrent.futures模块提供了ThreadPoolExecutor类来实现线程池。

import concurrent.futures


def task(num):
    print(f"线程 {threading.current_thread().name} 开始执行任务 {num}")
    import time
    time.sleep(1)
    print(f"线程 {threading.current_thread().name} 完成任务 {num}")
    return num * num


# 创建线程池,最大线程数为3
with concurrent.futures.ThreadPoolExecutor(max_workers = 3) as executor:
    # 提交任务
    future_to_num = {executor.submit(task, i): i for i in range(5)}
    for future in concurrent.futures.as_completed(future_to_num):
        num = future_to_num[future]
        try:
            result = future.result()
            print(f"任务 {num} 的结果: {result}")
        except Exception as e:
            print(f"任务 {num} 发生异常: {e}")

在这个示例中,我们使用ThreadPoolExecutor创建了一个线程池,最大线程数为 3。然后,我们使用submit方法向线程池提交任务,并使用as_completed方法获取已完成的任务结果。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

十年后LabVIEW编程知识是否会过时?

在考虑LabVIEW编程知识在未来十年内的有效性时,我们可以从几个角度进行分析: ​ 1. 技术发展与软件更新 随着技术的快速发展,许多编程工具和平台不断更新和改进,LabVIEW也不例外。十年后,可能会有新的编程语言或平台…

注册中心如何选型?Eureka、Zookeeper、Nacos怎么选

这是小卷对分布式系统架构学习的第9篇文章,第8篇时只回答了注册中心的工作原理的内容,面试官的第二个问题还没回答,今天再来讲讲各个注册中心的原理,以及区别,最后如何进行选型 上一篇文章:如何设计一个注册…

C++ 复习总结记录三

C 复习总结记录三 主要内容 1、类的六个默认成员函数 2、构造函数 3、析构函数 4、拷贝构造函数 5、赋值运算符重载 6、const 成员函数 7、取地址及 const 取地址操作符重载 一 类的六个默认成员函数 如果一个类中什么成员都没有,简称为空类。空类中并不是…

【简博士统计学习方法】第1章:4. 模型的评估与选择

4. 模型的评估与选择 4.1 训练误差与测试误差 假如存在样本容量为 N N N的训练集,将训练集送入学习系统可以训练学习得到一个模型,我们将这么模型用决策函数的形式表达,也就是 y f ^ ( x ) y\hat{f}(x) yf^​(x),关于模型的拟合…

计算机网络 (30)多协议标签交换MPLS

前言 多协议标签交换(Multi-Protocol Label Switching,MPLS)是一种在开放的通信网上利用标签引导数据高速、高效传输的新技术。 一、基本概念 MPLS是一种第三代网络架构技术,旨在提供高速、可靠的IP骨干网络交换。它通过将IP地址映…

【Java】JVM内存相关笔记

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。 程序计数器&am…

鸿蒙 ArkUI实现地图找房效果

常用的地图找房功能,是在地图上添加区域、商圈、房源等一些自定义 marker,然后配上自己应用的一些筛选逻辑构成,在这里使用鸿蒙 ArkUI 简单实现下怎么添加区域/商圈、房源等 Marker. 1、开启地图服务 在华为开发者官网,注册应用&…

STM32-WWDG/IWDG看门狗

WWDG/IWDG一旦开启不能关闭,可通过选项字节在上电时启动硬件看门狗,看门狗计数只能写入不能读取。看门狗启用时,T6bit必须置1,防止立即重置。 一、原理 独立看门狗-超时复位 窗口看门狗-喂狗(重置计数器,…

基于JAVA+SSM的车辆运输管理

基于JAVASSM的车辆运输管理 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末附源码下载链接🍅 哈喽兄弟们&#…

【Linux系列】Vim 编辑器中的高效文本编辑技巧:删除操作

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

win10 VS2019上libtorch库配置过程

win10 VS2019上libtorch库配置过程 0 引言1 获取libtorch2 在VS上配置使用libtorch库3 结语 0 引言 💻💻AI一下💻💻   libtorch库是一个用于深度学习的C库,是PyTorch的官方C前端。它提供了用于构建和训练深度学习模…

通过gradle发布aar或jar携带sources-jar到maven nexus

找了很久,没有找到满意的。终于找到一个好的办法。 gradle7.x适用。比以前的写法简洁。 发布传统的jar工程 比如okhttp,fastjson等项目,纯java工程。 直接创建新文件publish.gradle: apply plugin: maven-publishProperties properties …

STM32-笔记38-I2C-oled实验

一、什么是I2C? I2C总线,全称Inter-Integrated Circuit(互连集成电路),是一种由Philips(现NXP半导体)公司在1980年代初开发的同步 串行 半双工通信总线。 二、有了串口通信为什么要使用I2C&…

【Linux系列】并发与顺序执行:在 Linux 脚本中的应用与选择

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

“AI 视频图像识别系统,开启智能新视界

咱老百姓现在的生活啊,那是越来越离不开高科技了,就说这 AI 视频图像识别系统,听起来挺高大上,实际上已经悄无声息地融入到咱们日常的方方面面,给咱带来了超多便利。 先讲讲安防领域吧,这可是 AI 图像识别的…

Burpsuite20241102macM1版安装

1、安装jdk11 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" brew update brew install openjdk11 echo export PATH"/opt/homebrew/opt/openjdk11/bin:$PATH" >> ~/.zshrc source ~/.zshrc j…

NVIDIA在CES 2025上的三大亮点:AI芯片、机器人与自动驾驶、全新游戏显卡

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

PDFMathTranslate: Star13.8k,一款基于AI的PDF文档全文双语翻译PDF文档全文双语翻译,保留格式神器,你应该需要它

嗨,大家好,我是小华同学,关注我们获得“最新、最全、最优质”开源项目和高效工作学习方法 PDFMathTranslate是一个开源项目,旨在为用户提供便捷的PDF科学论文翻译解决方案。它不仅能够翻译文本,还能保留公式、图表、目…

h264之多视点mvc编码及解码过程(JMVC平台举例)

h264标准参考平台JMVC是针对MVC标准的,JMVC支持多视点编码、合流、多视点解码操作。可以利用JMVC生成h264 mvc码流和解码。 JMVC的下载地址是:jvet / JMVC GitLabH.264/AVC multi-view coding (MVC) extension JMVC reference softwarehttps://vcgit.hh…

LabVIEW软件侵权分析与应对

问:如果涉及到LabVIEW软件的仿制或模仿,特别是在功能、界面等方面,如何判断是否构成侵权?该如何应对? 答:LabVIEW软件的侵权问题,尤其是在涉及到仿制或模仿其功能、界面、设计等方面&#xff0…