线程(Thread)是操作系统能够进行 运算调度 的最小单位。它存在于进程内部,是 进程的一部分。
一个进程可以包含多个线程,这些线程共享 内存空间 和 资源,但能并发执行代码。
线程的主要优势是:
优点 | 说明 |
---|---|
并发执行 | 可以“同时”做多件事(比如边接收数据边处理) |
占用资源少 | 比进程轻量很多 |
更快响应 | 适合 I/O 密集型任务(如网络、文件读写) |
⚠️ 在 Python 中,由于 GIL(全局解释器锁),多线程不适合 CPU 密集型 运算(推荐多进程),但对于 I/O密集 非常有效!
import time
def task(name):
for i in range(3):
print(f"{name} is working...")
time.sleep(1)
task("A")
task("B")
⏱ 输出:会等 A 执行完,再执行 B(串行)
import threading
import time
def task(name):
for i in range(3):
print(f"{name} is working...")
time.sleep(1)
t1 = threading.Thread(target=task, args=("A",))
t2 = threading.Thread(target=task, args=("B",))
t1.start()
t2.start()
t1.join()
t2.join()
⏱ 输出:A 和 B 会交错执行(并发)
概念 | 内容 |
---|---|
线程 | 一个执行单元,存在于进程中 |
多线程意义 | 同时执行多个任务,提高效率 |
threading模块 | Python 内置模块,用于创建和管理线程 |
Python 中用 threading.Thread
创建线程有两种主要方式:
import threading
def say_hello():
print("Hello from thread!")
# 创建线程
t = threading.Thread(target=say_hello)
# 启动线程
t.start()
# 等待线程结束(可选)
t.join()
target=
:线程执行的函数名。args=()
:函数参数。start()
:启动线程。join()
:阻塞主线程,等待该线程执行完。import threading
class MyThread(threading.Thread):
def run(self):
print("Hello from subclass thread!")
t = MyThread()
t.start()
t.join()
方法 | 是否直接执行? | 是否阻塞主线程? |
---|---|---|
start() |
启动线程(异步) | 否 |
run() |
在主线程执行(同步) | 是 |
join() |
等待线程结束 | 是(阻塞) |
import threading
import time
def count_down(name):
for i in range(3, 0, -1):
print(f"{name} counting down: {i}")
time.sleep(1)
t1 = threading.Thread(target=count_down, args=("Thread-A",))
t2 = threading.Thread(target=count_down, args=("Thread-B",))
t1.start()
t2.start()
print("Main thread continues running...")
t1.join()
t2.join()
print("All threads done.")
⏱ 输出会交错,说明线程在并发执行。
def greet(name, delay):
time.sleep(delay)
print(f"Hello {name}, waited {delay}s!")
t = threading.Thread(target=greet, args=("Alice", 2))
t.start()
args=(param1, param2, ...)
传入。下面这段代码会输出什么顺序?你可以试着运行或猜一猜:
import threading
import time
def worker():
time.sleep(1)
print("Worker done.")
t = threading.Thread(target=worker)
t.start()
print("Main thread end.")
方法 | 说明 |
---|---|
Thread(target=func) |
创建线程,执行函数 func |
start() |
启动线程(非阻塞) |
join() |
等待线程执行结束(阻塞) |
args |
向目标函数传参 |
子类化 Thread | 创建更复杂线程(可重写 run() ) |
当多个线程访问“共享资源”(如同一个变量、文件、socket)时,如果没有同步机制,会出现竞态条件,导致程序结果不确定,甚至崩溃。
import threading
import time
counter = 0
def add():
global counter
for _ in range(1000):
tmp = counter
time.sleep(0.001) # 强制增加线程切换的机会
tmp += 1
counter = tmp
t1 = threading.Thread(target=add)
t2 = threading.Thread(target=add)
t1.start()
t2.start()
t1.join()
t2.join()
print("Counter:", counter) # ❌ 很容易 < 2000
temp+= 1
并不是原子操作,多个线程执行时会互相覆盖。threading.Lock
)import threading
import time
counter = 0
lock = threading.Lock()
def add():
global counter
for _ in range(1000):
with lock:
tmp = counter
time.sleep(0.001) # 强制增加线程切换的机会
tmp += 1
counter = tmp
t1 = threading.Thread(target=add)
t2 = threading.Thread(target=add)
t1.start()
t2.start()
t1.join()
t2.join()
print("Counter:", counter) ## ✅ 正确:2000
with lock:
会自动:lock.acquire()
)lock.release()
).acquire()
和 .release()
用法lock = threading.Lock()
lock.acquire()
try:
# 访问共享资源
...
finally:
lock.release()
不推荐自己写 acquire/release
,更推荐 with lock:
。
import threading
log_lock = threading.Lock()
def write_log(msg):
with log_lock:
with open("log.txt", "a") as f:
f.write(msg + "\n")
def worker(i):
for _ in range(10):
write_log(f"Thread {i} logging...")
threads = [threading.Thread(target=worker, args=(i,)) for i in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()
所有线程写入的日志不会相互干扰。
方法 | 含义 |
---|---|
threading.Lock() |
创建锁对象 |
lock.acquire() |
主动上锁(阻塞) |
lock.release() |
主动解锁 |
with lock: |
推荐写法,自动加解锁 |
有两种方式:
setDaemon(True)
daemon=True
(Python 3.3+)import threading
import time
def background_task():
while True:
print("后台线程运行中...")
time.sleep(1)
t = threading.Thread(target=background_task)
t.setDaemon(True) # 设置为守护线程
t.start()
print("主线程结束,守护线程会随之结束")
或者:
t = threading.Thread(target=background_task, daemon=True)
t.start()
join()
等待执行完成。import threading
import time
def task():
for i in range(5):
print(f"工作中...{i}")
time.sleep(1)
t = threading.Thread(target=task)
t.start()
print("主线程结束")
输出:
主线程结束
工作中...0
工作中...1
工作中...2
工作中...3
工作中...4
程序会等待 t
线程完成,才真正结束。
t = threading.Thread(target=task, daemon=True)
t.start()
print("主线程结束")
输出:
主线程结束
task
线程未运行完,程序直接退出了。
线程类型 | 程序退出时行为 |
---|---|
非守护线程 | 程序等待线程结束 |
守护线程 | 程序不等待,线程被强制结束 |
多个线程如果互不通信,就像工厂里几个工人干着不同的活但互不配合,很容易混乱。线程间的数据交互和信号同步能让它们“协作”工作,提升效率与安全。
方法 | 用途 | 是否线程安全 |
---|---|---|
全局变量 + 锁 | 手动控制共享数据访问 | ✔(加锁) |
queue.Queue |
自动化数据队列,线程安全 | ✔ |
threading.Event |
线程间的信号传递 | ✔ |
queue.Queue
:线程安全的数据共享队列Queue
是标准库中的线程安全队列,适合用于 生产者-消费者模型。
import threading
import queue
import time
q = queue.Queue()
def producer():
for i in range(5):
print(f" 生产者:放入 {i}")
q.put(i)
time.sleep(1)
def consumer():
while True:
item = q.get()
print(f" 消费者:取出 {item}")
q.task_done()
if item == 4:
break
threading.Thread(target=producer).start()
threading.Thread(target=consumer).start()
threading.Event
:线程之间传“信号”Event
就像一个红绿灯,线程 A 可以等待信号,线程 B 决定什么时候放行。
import threading
import time
event = threading.Event()
def waiter():
print(" 等待信号中...")
event.wait() # 阻塞直到被 set()
print("✅ 收到信号,开始执行任务")
def signaler():
time.sleep(3)
print(" 发出信号!")
event.set() # 唤醒等待的线程
threading.Thread(target=waiter).start()
threading.Thread(target=signaler).start()
多个线程读写同一个变量,可能会造成数据不一致。用 Lock
可以确保同一时间只有一个线程访问变量。
import threading
import time
lock = threading.Lock()
counter = 0
def add():
global counter
for _ in range(5):
with lock:
tmp = counter
tmp += 1
time.sleep(0.1)
counter = tmp
print(f"当前计数值: {counter}")
t1 = threading.Thread(target=add)
t2 = threading.Thread(target=add)
t1.start()
t2.start()
t1.join()
t2.join()
场景 | 推荐方法 |
---|---|
一线程发数据,一线程收数据 | queue.Queue |
一个线程通知多个线程启动 | threading.Event |
多线程同时读写同一变量 | Lock 或 RLock |
Queue
最安全,推荐用于线程之间数据传输。Event
适合做线程间的“信号通知”。