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

知微集:Python中的线程Thread(一)

欢迎来到"一起学点什么吧"的合集「NLP知微集」。在这里,我们不愿宏大叙事,只聚焦于自然语言处理领域中那些细微却关键的“齿轮”与“螺丝钉”。我相信,真正深刻的理解,源于对细节的洞察。本期,我将为您拆解的是:[Python线程]

关于Python线程的相关内容,比较多,准备分几期来一起探讨一下Python线程的问题。

Python线程

在Python中线程是什么样的?

线程是指计算机程序中的一个执行流。

每个程序都是一个进程,并且至少有一个线程来执行该进程的指令。

当我们运行 Python 脚本时,它启动 Python 解释器的实例,在主线程中运行我们的代码。主线程是 Python 进程的默认线程。

我们可以开发程序以并发执行任务,在这种情况下,我们可能需要创建和运行新的线程。这些将是程序之外的并发执行线程,例如:

  • 并发执行函数调用。
  • 并发执行对象方法。

Python 线程是底层操作系统提供的原生线程的对象表示。

当我们创建并运行一个新线程时,Python 将对底层操作系统进行系统调用,请求创建一个新线程并启动新线程的运行。

这表明 Python 线程是真正的线程,而不是模拟的软件线程。

在新线程中的代码可能会也可能不会并行执行(同时执行),尽管这些线程是并发执行的。造成这种现象的原因有很多,例如:

  • 底层硬件可能支持或不支持并行执行(例如,单个 CPU 核心与多个 CPU 核心)。
  • Python 解释器可能允许或不允许多个线程并行执行。

这突出了可以顺序执行(并发)的代码与能够同时执行(并行)的能力之间的区别。

  • 并发(Concurrent):可以交错执行的代码。
  • 并行(Parallel):同时执行代码的能力。

线程 vs 进程

一个进程指的是一个计算机程序。

每个进程实际上都是 Python 解释器的一个实例,该实例执行 Python 指令(Python 字节码),这比你在 Python 程序中输入的代码要低一个层次。

底层操作系统控制新进程的创建方式。在某些系统上,这可能需要创建一个新进程,而在其他系统上,这可能需要进程进行分叉。在 Python 中用于创建新进程的操作系统特定方法不是我们需要担心的事情,因为它由您安装的 Python 解释器管理。

线程始终存在于进程中,并代表指令或代码的执行方式。

Python 进程将在所有(非后台线程)终止后终止。

  • 进程(Process):Python 解释器的实例至少有一个名为 MainThread 的线程。
  • 线程(Thread):Python 进程中的一条执行线程,例如主线程或新创建的线程。

线程生命周期(Life-Cycle of a thread)

Python 中的线程表示为 threading.Thread 类的实例。

一旦线程启动,Python 解释器将与底层操作系统交互,请求创建一个新的原生线程。然后,threading.Thread 实例为该底层原生线程提供一个基于 Python 的引用。

每个线程都遵循相同的生命周期。理解这个生命周期的各个阶段,有助于在 Python 中开始并发编程。

一个 Python 线程可能经历其生命周期中的三个步骤:新线程、运行中的线程和已终止的线程。
生命周期

在线程中运行函数

Python 函数可以使用 threading.Thread 类在单独的线程中执行。

如何在线程中运行函数

  • 创建 threading.Thread 类的实例。
  • 通过“target”参数指定函数的名称。
  • 调用 start() 函数。

在另一个线程中执行的函数可能有参数,在这种情况下,可以将它们指定为一个元组,并传递给 threading.Thread 类构造函数的“args”参数,或者将它们作为一个字典传递给“kwargs”参数。

start()函数将立即返回,操作系统将在其能够执行时立即在单独的线程中执行该函数。

我们无法精确控制线程何时执行或由哪个 CPU 核心执行。这两项都是底层操作系统处理的职责。

在一个线程中运行函数的示例

# 在另一个线程中运行函数的示例
from time import sleep
from threading import Thread# 一个自定义函数,会阻塞一段时间
def task():# 阻塞一段时间sleep(1)# 显示消息print('This is from another thread')# 创建一个线程
thread = Thread(target=task)
# 运行线程
thread.start()
# 等待线程完成
print('Waiting for the thread...')
thread.join()
# 可以通过调用 join() 函数显式地等待新线程执行完成。这并非必需的,因为主线程不会在新线程完成之前退出。# Waiting for the thread...
# This is from another thread

在线程中运行带参数的函数示例

# 示例:在另一个线程中运行带参数的函数
from time import sleep
from threading import Thread# 一个自定义函数,会阻塞一段时间
def task(sleep_time, message):# 阻塞一段时间sleep(sleep_time)# 显示消息print(message)# 创建一个线程
thread = Thread(target=task, args=(1.5, 'New message from another thread'))
# 启动线程
thread.start()
# 等待线程完成
print('Waiting for the thread...')
thread.join()# Waiting for the thread...
# New message from another thread

扩展 Thread 类

可以通过扩展 threading.Thread 类并重写 run()函数来在另一个线程中执行函数。

如何扩展 Thread 类

可以通过扩展 threading.Thread 类来在另一个线程中运行代码。

# custom thread class
class CustomThread(Thread):# ...# 重写 threading.Thread 类的 run()函数,以包含希望在另一个线程中执行的代码。# override the run functiondef run(self):# ...

由于这是一个自定义类,您可以为其定义一个构造函数,并使用它来传入 run() 函数中可能需要的数据,并将其存储为实例变量(属性)。

可以在类上定义其他函数,以便将您可能需要在另一个线程中完成的工作分开处理。

最后,属性也可以用来存储在其他线程中执行的任何计算或 IO 的结果,这些结果可能需要在之后检索。

扩展线程类的示例

创建一个 CustomThread 类的实例,并调用 start()函数以在另一个线程中开始执行我们的 run()函数。在内部,start()函数将调用 run()函数。

# 示例:继承Thread类
from time import sleep
from threading import Thread# 自定义线程类
class CustomThread(Thread):# 重写run函数def run(self):# 阻塞一段时间sleep(1)# 显示消息print('This is coming from another thread')# 创建线程
thread = CustomThread()
# 启动线程
thread.start()
# 等待线程完成
print('Waiting for the thread to finish')
thread.join()

扩展线程类并返回值的示例

我们可能需要从线程中检索数据,例如返回值。

run() 函数没有办法将值返回给 start() 函数再返回给调用者。

相反,我们可以通过将它们存储为实例变量,并让调用者从这些实例变量中检索数据,从而从我们的 run() 函数中返回值。

可以更新 run() 函数以将某些数据存储为实例变量(也称为 Python 属性)。

# 示例:继承Thread类并返回值
from time import sleep
from threading import Thread# 自定义线程类
class CustomThread(Thread):# 重写run函数def run(self):# 阻塞一段时间sleep(1)# 显示消息print('This is coming from another thread')# 存储返回值self.value = 99# 创建线程
thread = CustomThread()
# 启动线程
thread.start()
# 等待线程完成
print('Waiting for the thread to finish')
thread.join()
# 获取从run返回的值
value = thread.value
print(f'Got: {value}')

返回多个值

# 示例:从线程返回多个值
from time import sleep
from threading import Thread# 自定义线程
class CustomThread(Thread):# 构造函数def __init__(self):# 执行基类构造函数Thread.__init__(self)# 设置默认值self.value1 = Noneself.value2 = Noneself.value3 = None# 在新线程中执行的函数def run(self):# 阻塞一段时间sleep(1)# 在实例变量中存储数据self.value1 = 'Hello from a new thread'self.value2 = 99self.value3 = False# 创建新线程
thread = CustomThread()
# 启动线程
thread.start()
# 等待线程完成
thread.join()
# 报告从线程返回的所有值
print(thread.value1)
print(thread.value2)
print(thread.value3)

全局变量返回值

# 示例:从线程返回一个值
from time import sleep
from threading import Thread# 在新线程中执行的函数
def task():# 阻塞一段时间sleep(1)# 正确作用域全局变量global data# 在全局变量中存储数据data = 'Hello from a new thread'# 定义全局变量
data = None
# 创建新线程
thread = Thread(target=task)
# 启动线程
thread.start()
# 等待线程完成
thread.join()
# 报告全局变量
print(data)

新线程随后将数据存储在全局变量中,确保显式指定作用域,以避免在阅读代码时产生混淆以及未来可能出现的错误。

主线程在新的线程终止之前阻塞,然后报告存储在全局变量中的模拟返回值。

Python线程属性图

ThreadingMap
ThreadingPython

结语

“见微知著,积跬步以至千里”,至此,关于 [Python线程] 的微探索之旅才缓缓拉开序幕,后续将一起详细去探索一下Python的线程相关问题。感谢您的耐心阅读。希望这片小小的“知微”碎片,能让你对Python线程有更清晰的认识。

点赞关注不迷路,点击合集标签「#NLP知微集」,不错过每一次细微的洞察。

下期再见,继续我们的拆解之旅!

Reference

  • https://superfastpython.com/threading-in-python/
http://www.dtcms.com/a/364890.html

相关文章:

  • MCP是什么? 小白如何学习使用MCP?一篇文档带你详细了解神秘的MCP
  • 【C++】控制台输入与输出
  • osgb转b3dm转glb小工具流程-解决办法
  • Linux内核进程管理子系统有什么第四十一回 —— 进程主结构详解(37)
  • 《用 Flask 构建用户认证系统:从零开始实现注册与登录功能》
  • 10.2 工程学中的矩阵(2)
  • 基于Docker和Kubernetes的CI/CD流水线架构设计与优化实践
  • 加解密安全-侧信道攻击
  • AI Waifu RAT:新型恶意软件伪装AI助手劫持用户电脑
  • OpenLayers常用控件 -- 章节一:地图缩放控件详解教程
  • Java 反序列化漏洞
  • 专题四_前缀和_一维前缀和
  • JAVA 十二幕啦啦啦啦啦啦啦啊啦啦啦啦a
  • AI IDE+AI 辅助编程,真能让程序员 “告别 996” 吗?
  • 【Java】对于XML文档读取和增删改查操作与JDBC编程的读取和增删改查操作的有感而发
  • 面试题:JVM与G1要点总结
  • 告别 Hadoop,拥抱 StarRocks!政采云数据平台升级之路
  • 海思HI3516CV610-20S,HI3516CV610专为安防市场超高清智慧视觉SoC硬件设计的一款开发板
  • MongoDB 聚合查询超时:索引优化与分片策略的踩坑记录
  • Prometheus监控预警系统深度解析:架构、优劣、成本与竞品
  • CryptMsgGetParam函数分析之CMSG_INNER_CONTENT_TYPE_PARAM
  • 110个作品涨粉210万!用Coze智能体工作流1分钟生成爆款名著金句视频,无需剪辑,附详细教程
  • 【FastDDS】Layer DDS之Domain (01-overview)
  • 限流式保护器+安全用电云平台如何为企业安全用电做双重防护的?
  • 机器学习从入门到精通 - 手撕线性回归与梯度下降:从数学推导到Scikit-Learn实战
  • Scikit-learn Python机器学习 - 特征预处理 - 处理缺失值:SimpleImputer
  • 深度学习与 OpenCV 的深度羁绊:从技术协同到代码实践
  • 苍穹外卖项目实战(日记十四)-记录实战教程及问题的解决方法-(day3课后作业) 菜品停售启售功能
  • centos 压缩命令
  • 解决CentOS 镜像列表服务已下线或迁移导致镜像服务和仓库停止维护解决方案