跳到主要内容

多线程基础

运维脚本常需同时检查多台主机、多个 URL 或多个 API。Python threading 模块适合 I/O 密集型任务(网络请求、SSH、读文件),可在等待响应时切换线程,提高吞吐。CPU 密集计算仍应使用多进程,本文不展开。


核心概念

概念说明
Thread操作系统调度的轻量执行单元
GIL全局解释器锁,同一时刻仅一个线程执行 Python 字节码
I/O 释放 GIL网络/磁盘等待时 GIL 会释放,多线程仍有效

创建与启动线程

#!/usr/bin/env python3
"""为每台主机启动独立线程执行 ping 检查(示例用 sleep 模拟 I/O)。"""

import socket
import threading
import time
from typing import Callable


def check_host(ip: str, results: list, lock: threading.Lock) -> None:
"""模拟远程检查:解析主机名 + 等待响应。"""
try:
hostname = socket.gethostbyaddr(ip)[0]
time.sleep(0.5) # 模拟 SSH/HTTP 延迟
status = "ok"
except OSError:
hostname = "unknown"
status = "fail"

# 多线程写共享列表必须加锁
with lock:
results.append({"ip": ip, "hostname": hostname, "status": status})


def run_checks(hosts: list[str]) -> list[dict]:
results: list[dict] = []
lock = threading.Lock()
threads: list[threading.Thread] = []

for ip in hosts:
t = threading.Thread(target=check_host, args=(ip, results, lock), name=f"check-{ip}")
threads.append(t)
t.start()

# 等待全部线程结束
for t in threads:
t.join()

return results


def main() -> None:
hosts = ["10.0.0.1", "10.0.0.2", "10.0.0.3"]
report = run_checks(hosts)
for row in report:
print(row)


if __name__ == "__main__":
main()

Lock 与线程安全

counter = 0
lock = threading.Lock()


def increment() -> None:
global counter
for _ in range(100_000):
with lock: # 保证 read-modify-write 原子性
counter += 1

不加锁时 counter += 1 可能丢失更新。共享状态(列表、字典、计数器)写入时务必同步。


Event 与超时控制

def worker(stop_event: threading.Event) -> None:
while not stop_event.is_set():
# 周期性巡检逻辑
time.sleep(1)
print("收到停止信号,退出")


def run_with_timeout(seconds: float) -> None:
stop = threading.Event()
t = threading.Thread(target=worker, args=(stop,))
t.start()
time.sleep(seconds)
stop.set() # 通知线程结束
t.join(timeout=5)

Event 适合优雅停止后台巡检线程;join(timeout=...) 防止主线程无限等待。


何时用原生线程

手动 Thread 适合:

  • 少量固定后台任务(日志采集、心跳)
  • 学习并发模型

批量同类任务(上百台主机 HTTP 探活)更推荐下一章的 ThreadPoolExecutor,可限制并发数、复用线程、统一收集异常。

提示
  • 默认线程为 daemon=False,主程序会等线程结束;守护线程随主进程退出
  • 调试时给线程 name= 便于日志定位
  • 线程数不是越多越好,过多会增加上下文切换与目标端压力

小结

  • I/O 密集型运维任务可用 threading 提速
  • 共享数据写操作必须加 Lock
  • 批量任务优先用线程池(下一章)

下一章:ThreadPoolExecutor 实现批量主机巡检。