导航菜单

  • 1.什么是MCP
  • 2.MCP架构
  • 3.MCP服务器
  • 4.MCP客户端
  • 5.版本控制
  • 6.连接MCP服务器
  • 7.SDKs
  • 8.Inspector
  • 9.规范
  • 10.架构
  • 11.协议
  • 12.生命周期
  • 13.工具
  • 14.资源
  • 15.提示
  • 16.日志
  • 17.进度
  • 18.传输
  • 19.补全
  • 20.引导
  • 21.采样
  • 22.任务
  • 23.取消
  • 24.Ping
  • 25.根
  • 26.分页
  • 27.授权
  • 28.初始化
  • 29.工具
  • 30.资源
  • 31.结构化输出
  • 32.提示词
  • 33.上下文
  • 34.StreamableHTTP
  • 35.参数补全
  • 36.引导
  • 37.采样
  • 38.LowLevel
  • 39.任务
  • 40.取消
  • 41.ping
  • 42.根
  • 43.分页
  • 44.授权
  • 45.授权
  • Keycloak
  • asyncio
  • contextlib
  • httpx
  • pathlib
  • pydantic
  • queue
  • starlette
  • subprocess
  • threading
  • uvicorn
  • JSON-RPC
  • z
  • 1. 什么是线程?
    • 1.1 什么是线程?
    • 1.2 为什么需要多线程?
  • 2. 什么是 threading?
  • 3. 创建线程
    • 3.1 最常用的方式:传入函数
    • 3.2 继承 Thread 类(可选)
  • 4. 线程的常用属性和方法
  • 5. 线程同步:锁(Lock)
    • 5.1 为什么需要锁?
    • 5.2 使用锁保护共享数据
  • 6. 事件(Event):线程间简单信号
  • 7. 线程间通信:队列(Queue)
  • 8. 守护线程
  • 9. GIL 简要说明
  • 10. 注意事项
  • 11. 总结

1. 什么是线程? #

1.1 什么是线程? #

进程是程序运行时的"容器",每个进程有独立的内存空间。线程是进程内的"执行单元",一个进程可以包含多个线程,它们共享进程的内存。

主线程:Python 程序启动时默认有一个线程在跑,这就是主线程。你写的代码默认都在主线程里执行。

1.2 为什么需要多线程? #

程序默认是单线程的,代码一行行执行。如果某一步要等很久(比如等网络响应、读大文件),后面的代码就得干等着。多线程可以让程序"同时"做多件事:一个线程在等 I/O,另一个线程可以继续干活,提高效率。

通俗比喻:单线程像一个人排队办业务;多线程像开多个窗口,多人同时办。

注意:Python 有 GIL(全局解释器锁),多线程不能真正并行执行计算,但适合 I/O 密集型任务(网络、磁盘读写等),因为等待 I/O 时会释放 GIL,其他线程可以运行。

2. 什么是 threading? #

threading 是 Python 标准库中的多线程模块,提供:

  • 创建线程:Thread 类
  • 同步机制:锁(Lock)、事件(Event)等,防止多线程同时改同一份数据
  • 线程间通信:配合 queue.Queue 传递数据

下面从创建线程开始学习。

3. 创建线程 #

3.1 最常用的方式:传入函数 #

把要执行的函数传给 Thread,用 args 传参数。

# 导入 threading 和 time
import threading
import time

# 定义线程要执行的函数
def worker(name, delay):
    # 打印开始信息
    print(f"线程 {name} 开始")
    # 模拟耗时操作(如等待网络、读文件)
    time.sleep(delay)
    # 打印结束信息
    print(f"线程 {name} 结束")

# 创建两个线程,target 是函数名,args 是传给函数的参数(元组)
t1 = threading.Thread(target=worker, args=("A", 2))
t2 = threading.Thread(target=worker, args=("B", 1))

# 启动线程(启动后会自动调用 worker 函数)
t1.start()
t2.start()

# 主线程等待 t1 和 t2 都执行完再继续
t1.join()
t2.join()

# 所有线程结束后打印
print("所有线程已完成")

说明:线程 A 睡 2 秒,线程 B 睡 1 秒,B 会先结束。join() 会阻塞主线程,直到被等待的线程结束。

3.2 继承 Thread 类(可选) #

通过继承 Thread 并重写 run() 方法,也可以自定义线程行为。一般用 3.1 的方式就够了。

# 导入 threading 和 time
import threading
import time

# 继承 Thread 类
class MyThread(threading.Thread):
    # 自定义初始化,接收 name 和 delay
    def __init__(self, name, delay):
        # 先调用父类初始化
        super().__init__()
        self.name = name
        self.delay = delay

    # 重写 run 方法,线程启动后会执行这里的代码
    def run(self):
        print(f"线程 {self.name} 开始")
        time.sleep(self.delay)
        print(f"线程 {self.name} 结束")

# 创建并启动线程
t = MyThread("C", 1.5)
t.start()
t.join()
print("线程 C 已完成")

4. 线程的常用属性和方法 #

方法/属性 作用
start() 启动线程
join(timeout) 等待线程结束,可设置超时秒数
name 线程名称,可读可写
is_alive() 线程是否还在运行
daemon 是否为守护线程(需在 start() 前设置)
# 导入 threading 和 time
import threading
import time

# 定义线程函数
def worker():
    time.sleep(1)
    # 获取当前线程对象并打印名称
    print("当前线程:", threading.current_thread().name)

# 创建线程,指定名称
t = threading.Thread(target=worker, name="工作线程")
# 启动
t.start()
# 查看是否存活
print("线程是否存活:", t.is_alive())
# 等待结束
t.join()
print("线程是否存活:", t.is_alive())

5. 线程同步:锁(Lock) #

多个线程同时修改同一份数据时,可能产生数据错乱。锁可以保证同一时刻只有一个线程执行某段代码。

5.1 为什么需要锁? #

下面是不加锁时,多线程同时修改计数器,结果可能不对:

# 导入 threading
import threading

# 共享变量:计数器
counter = 0

# 每个线程执行 10 万次加 1
def increment():
    global counter
    for _ in range(100000):
        counter += 1

# 创建 10 个线程同时加
threads = [threading.Thread(target=increment) for _ in range(10)]
for t in threads:
    t.start()
for t in threads:
    t.join()

# 理想结果是 1000000,实际可能小于 1000000(因为数据竞争)
print("counter =", counter)

5.2 使用锁保护共享数据 #

用 Lock 包住"读-改-写"这段代码,保证同一时刻只有一个线程执行。

# 导入 threading
import threading

# 共享变量
counter = 0
# 创建锁
lock = threading.Lock()

# 每个线程执行 10 万次加 1
def increment():
    global counter
    for _ in range(100000):
        # with lock 会自动获取锁,执行完后自动释放
        # 保证 counter += 1 同一时刻只有一个线程在执行
        with lock:
            counter += 1

# 创建 10 个线程
threads = [threading.Thread(target=increment) for _ in range(10)]
for t in threads:
    t.start()
for t in threads:
    t.join()

# 正确输出 1000000
print("counter =", counter)

新手提示:推荐始终用 with lock:,这样即使发生异常,锁也会被正确释放。

6. 事件(Event):线程间简单信号 #

Event 用于一个线程"通知"另一个线程:某个条件已满足,可以继续了。

# 导入 threading 和 time
import threading
import time

# 创建事件对象
event = threading.Event()

# 等待事件的线程
def waiter():
    print("等待事件触发...")
    # 阻塞,直到 event.set() 被调用
    event.wait()
    print("事件已触发,继续执行")

# 触发事件的线程
def setter():
    # 模拟准备时间
    time.sleep(2)
    print("触发事件")
    # 设置事件,所有 wait() 的线程会继续
    event.set()

# 启动两个线程
threading.Thread(target=waiter).start()
threading.Thread(target=setter).start()

说明:event.wait() 会阻塞直到别的线程调用 event.set()。适合"准备好了再通知"这类场景。

7. 线程间通信:队列(Queue) #

多个线程之间传递数据时,用 queue.Queue 最安全。一个线程往队列里放数据,另一个线程从队列里取,无需自己加锁。

# 导入 threading、queue 和 time
import threading
import queue
import time

# 创建队列
q = queue.Queue()

# 生产者:往队列里放数据
def producer():
    for i in range(5):
        q.put(f"数据-{i}")
        print(f"生产 数据-{i}")
        time.sleep(0.5)
    # 放入 None 作为结束信号
    q.put(None)

# 消费者:从队列里取数据
def consumer():
    while True:
        item = q.get()
        # 收到结束信号就退出
        if item is None:
            break
        print(f"消费 {item}")

# 创建并启动生产者和消费者线程
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start()
t2.start()
# 等待两个线程结束
t1.join()
t2.join()
print("完成")

说明:Queue 是线程安全的,多个线程同时 put 和 get 不会出错。更详细的用法见 queue 模块教程。

8. 守护线程 #

守护线程:当所有非守护线程结束时,程序会退出,不等待守护线程完成。适合做后台任务(如心跳、日志)。

# 导入 threading 和 time
import threading
import time

# 模拟后台任务
def daemon_worker():
    for i in range(5):
        print(f"守护线程运行中 {i}")
        time.sleep(1)
    print("守护线程结束")

# 创建线程,daemon=True 表示守护线程
t = threading.Thread(target=daemon_worker, daemon=True)
t.start()

# 主线程只等 2 秒
time.sleep(2)
print("主线程结束,程序退出(守护线程可能未跑完)")

注意:daemon 必须在 start() 之前设置。守护线程可能被突然终止,不要用它做必须完成的任务。

9. GIL 简要说明 #

GIL(全局解释器锁)是 CPython 的一个机制,同一时刻只允许一个线程执行 Python 字节码。因此:

  • CPU 密集型(大量计算):多线程无法真正并行,反而可能更慢,应改用 multiprocessing(多进程)
  • I/O 密集型(网络、磁盘):线程在等待 I/O 时会释放 GIL,多线程能提高效率,threading 很适合

新手只需记住:多线程适合 I/O 等待多的场景,不适合纯计算密集场景。

10. 注意事项 #

  1. 共享数据要加锁:多线程修改同一变量时,用 Lock 保护。
  2. 推荐用 with lock:避免忘记释放锁。
  3. 线程内异常不会传到主线程:在目标函数里自己 try/except 处理。
  4. 慎用守护线程:只在确定"可随时中断"的后台任务时使用。

11. 总结 #

内容 要点
创建线程 Thread(target=函数, args=(参数,)),然后 start()、join()
保护共享数据 用 Lock,配合 with lock:
线程间信号 用 Event:wait() 等待,set() 触发
线程间传数据 用 queue.Queue
守护线程 daemon=True,程序退出时不等待

记忆口诀:创建用 Thread,共享用 Lock,传信用 Event,传数用 Queue。

← 上一节 subprocess 下一节 uvicorn →

访问验证

请输入访问令牌

Token不正确,请重新输入