Python多线程怎么用_Python多线程基础入门教程

10次阅读

python线程适用于I/O密集型任务,但受GIL限制无法并行执行CPU密集型代码;启动线程需用Thread(target=func, args=(), kwargs={})后调start(),不可直接调用func();共享变量须用Lock保护临界区,避免竞态条件。

Python多线程怎么用_Python多线程基础入门教程

Python 多线程在 I/O 密集型任务中能提升响应速度,但受 GIL 限制,无法真正并行执行 CPU 密集型代码——别指望靠 threading 加快纯计算(比如大数组求和、加密解密),那得换 multiprocessingasyncio

怎么启动一个线程?用 threading.Thread 最直接

最常用方式是传入 target 函数和 args/kwargs 参数,调用 start() 启动,不是直接调函数。

常见错误:写成 t = Thread(target=func()) —— 这会立刻执行 func,且主线程阻塞等待返回值,根本没开线程。

正确写法示例:

立即学习Python免费学习笔记(深入)”;

import threading import time 

def say_hi(name, delay=1): time.sleep(delay) print(f"Hello, {name}")

t = threading.Thread(target=say_hi, args=("Alice",), kwargs={"delay": 0.5}) t.start() t.join()

  • args 必须是 tuple,单个参数记得加逗号:(value,),不是 (value)
  • join() 不调用的话,主线程可能提前退出,子线程被强制终止(尤其脚本末尾没等待时)
  • 线程名默认是 Thread-N,可通过 name="my_worker" 显式指定,方便调试

共享变量出问题?加 threading.Lock 保护临界区

多个线程同时读写同一个变量(比如全局计数器),不加锁会导致结果错乱——这不是概率问题,是必然发生,只是时机难复现。

典型现象:counter 本该累加 100 次到 100,结果输出 92、97、甚至 63。

解决方法:用 Lock 包裹修改共享数据的代码段:

import threading 

counter = 0 lock = threading.Lock()

def increment(): global counter for _ in range(10): with lock: # 自动 acquire/release counter += 1

threads = [threading.Thread(target=increment) for _ in range(10)] for t in threads: t.start() for t in threads: t.join()

print(counter) # 稳定输出 100

  • 别用 time.sleep() 模拟“耗时操作”来测试竞态条件——它反而可能掩盖问题;真要测,用循环空转或 os.write() 等低层调用
  • Lock 是不可重入的,同一线程重复 acquire() 会死锁;需要重入锁就用 R Lock
  • 锁粒度越小越好,只包真正共享修改的几行,别把整个函数都锁住

为什么 threading.Timer 有时不触发?注意对象生命周期

threading.Timer 启动后是个独立线程,但如果创建它的对象(比如类实例)被垃圾回收,而 Timer 又没被强引用,就可能被提前销毁,回调永远不执行。

常见场景:在类方法里写 Timer(2.0, self.callback).start(),但方法返回后实例被删,Timer 就失效了。

解决办法:

  • 把 Timer 对象存为实例属性:self.timer = Timer(...); self.timer.start()
  • 或用弱引用管理(较少见),或改用 threading.Event + 循环检查
  • 注意:Timer 只触发一次,要周期性执行请用循环 + Event.wait() 或改用 schedule

多线程真正的难点不在语法,而在判断哪些状态是共享的、哪些操作必须原子化、以及锁的持有范围是否覆盖了所有访问路径——一个漏掉的读或写,就可能让程序在高并发下间歇性崩掉,还很难复现。

text=ZqhQzanResources