Python 多线程核心:深入解析threading.Thread的run()方法
一、run() 方法的核心作用
run() 方法是 threading.Thread 类的一个内置成员,它定义了线程启动后需要执行的具体任务和逻辑。可以把 run() 方法看作是新线程的入口点或主函数。当启动一个线程时,操作系统会为这个线程分配独立的执行上下文,然后在这个新的上下文中调用的第一个方法就是 run()。run() 方法执行完毕,这个线程的生命周期也就结束了。
二、线程任务定义
定义线程任务主要有两种方式,但这两种方式都离不开 run() 方法。
方式一:通过 target 参数传递一个函数
对于简单的任务,专门创建一个子类可能显得有些繁琐。threading.Thread 的构造函数提供了一个 target 参数,允许你直接指定一个函数作为线程的执行体。
实现原理: 这种方式其实是 run() 方法的一种“快捷方式”。threading.Thread 类的默认 run() 方法内部实现逻辑大致是:检查用户是否在创建实例时提供了 target 函数,如果提供了,就去调用这个 target 函数。所以,run() 依然是那个入口点,只不过它把具体的执行任务“委托”给了你传入的函数。
代码示例:
import threading
import time
def download_file(filename, duration):
"""
一个模拟文件下载的函数,将作为线程的任务。
"""
print(f"线程 '{filename}':开始下载...")
for i in range(1, duration + 1):
print(f"线程 '{filename}':下载中... [{i * 25}%]")
time.sleep(0.5)
print(f"线程 '{filename}':下载完成。")
# --- 主程序 ---
print("主线程:准备开始下载任务...")
# 创建 Thread 实例,通过 target 指定要执行的函数
# 注意:target=download_file 是传递函数对象本身,不要写成 download_file()
# args 是一个元组,用来传递给 target 函数的参数
thread = threading.Thread(target=download_file, args=("电影.mp4", 4))
# 启动线程,默认的 run() 方法会调用我们的 download_file 函数
thread.start()
print("主线程:下载任务已在后台开始。")
thread.join()
print("主线程:下载任务结束。")
# --- 运行结果 ---
# 主线程:准备开始下载任务...
# 线程 '电影.mp4':开始下载...
# 线程 '电影.mp4':下载中... [25%]
# 主线程:下载任务已在后台开始。
# 线程 '电影.mp4':下载中... [50%]
# 线程 '电影.mp4':下载中... [75%]
# 线程 '电影.mp4':下载中... [100%]
# 线程 '电影.mp4':下载完成。
# 主线程:下载任务结束。
方式二:通过继承 threading.Thread 类并重写 run() 方法
这是一种非常经典的、面向对象的线程创建方式。它允许你将线程的逻辑封装在一个独立的类中,结构清晰。
实现步骤:
-
定义一个子类:创建一个新的类,让它继承自
threading.Thread。 -
重写
run()方法:在你的子类中,定义一个名为run()的方法。把你希望这个线程执行的所有代码都写在这个方法里。 -
实例化并启动:创建你自定义线程类的实例,然后调用它的
.start()方法来启动线程。
注意⚠️:启动一个新的线程,并在这个新线程中执行 run() 方法时,必须调用 start() 方法,而不是直接调用 run()。start() 方法是 Thread 类的一个内置方法,它会进行一些必要的初始化工作,然后在一个新的线程中调用你所重写的 run() 方法。
import threading
import time
class MyCustomThread(threading.Thread):
"""
一个自定义的线程类,通过重写 run() 方法来定义线程任务。
"""
def __init__(self, name, duration):
# 必须先调用父类的构造函数
super().__init__()
self.filename = name
self.duration = duration
def run(self):
"""
线程启动后,这里的代码会被自动执行。
"""
print(f"线程 '{self.filename}':开始下载...")
for i in range(1, self.duration + 1):
print(f"线程 '{self.filename}':下载中... [{i * 25}%]")
time.sleep(0.5)
print(f"线程 '{self.filename}':下载完成。")
# --- 主程序 ---
print("主线程:准备开始下载任务...")
# 创建我们自定义线程类的实例
thread = MyCustomThread(name="电影.mp4", duration=4)
# 调用 start() 方法启动线程,这会自动在新线程中执行 run() 方法
thread.start()
# 主线程可以继续执行其他任务,不会被阻塞
print("主线程:下载任务已在后台开始。")
# 使用 join() 方法可以等待子线程执行结束
thread.join()
print("主线程:下载任务结束。")
# --- 运行结果 ---
# 主线程:准备启动子线程...
# 线程 '电影.mp4':开始下载...
# 线程 '电影.mp4':下载中... [25%]
# 主线程:已经启动了子线程,现在我去做别的事情。
# 线程 '电影.mp4':下载中... [50%]
# 线程 '电影.mp4':下载中... [75%]
# 线程 '电影.mp4':下载中... [100%]
# 线程 '电影.mp4':下载完成。
# 主线程:子线程已结束,主程序退出。
三、内部工作流程
run() 的实现非常简单。在 threading.Thread 的源代码中,它的默认实现大致如下:
# threading.py 源代码的简化示意
class Thread:
# ... __init__, a bunch of other methods ...
def run(self):
"""Method representing the thread's activity."""
try:
if self._target:
self._target(*self._args, **self._kwargs)
finally:
# Avoid a refcycle if the thread is running a function with
# an argument that has a member that points to the thread.
del self._target, self._args, self._kwargs
可以看出:
-
它就是一个普通的 Python 方法
-
它的默认行为是检查是否存在一个
_target(也就是你通过target=参数传进来的函数),如果存在,就执行它 -
如果你通过继承
Thread类并重写run()方法,那么这个默认逻辑就会被你的新逻辑所取代
更多推荐


所有评论(0)