一、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() 方法

这是一种非常经典的、面向对象的线程创建方式。它允许你将线程的逻辑封装在一个独立的类中,结构清晰。

实现步骤:

  1. 定义一个子类:创建一个新的类,让它继承自 threading.Thread

  2. 重写 run() 方法:在你的子类中,定义一个名为 run() 的方法。把你希望这个线程执行的所有代码都写在这个方法里。

  3. 实例化并启动:创建你自定义线程类的实例,然后调用它的 .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

可以看出:

  1. 它就是一个普通的 Python 方法

  2. 它的默认行为是检查是否存在一个 _target (也就是你通过 target= 参数传进来的函数),如果存在,就执行它

  3. 如果你通过继承 Thread 类并重写 run() 方法,那么这个默认逻辑就会被你的新逻辑所取代

更多推荐