【python】线程专题

第 1 章:线程是什么?


1.1 什么是线程?

线程(Thread)是操作系统能够进行 运算调度 的最小单位。它存在于进程内部,是 进程的一部分

一个进程可以包含多个线程,这些线程共享 内存空间资源,但能并发执行代码。


‍♂️ 比喻理解

  • 进程:是一家工厂。
  • 线程:是工厂里的工人,他们在一起干活(共享资源),但每个人可以做不同的事情(独立运行)。

1.2 为什么使用线程?

线程的主要优势是:

优点 说明
并发执行 可以“同时”做多件事(比如边接收数据边处理)
占用资源少 比进程轻量很多
更快响应 适合 I/O 密集型任务(如网络、文件读写)

⚠️ 在 Python 中,由于 GIL(全局解释器锁),多线程不适合 CPU 密集型 运算(推荐多进程),但对于 I/O密集 非常有效!


1.3 单线程 vs 多线程示例

单线程:
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 内置模块,用于创建和管理线程

第 2 章:threading.Thread 基础用法


2.1 创建线程的两种方式

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():阻塞主线程,等待该线程执行完。

✅ 方法二:继承 Thread 类
import threading

class MyThread(threading.Thread):
    def run(self):
        print("Hello from subclass thread!")

t = MyThread()
t.start()
t.join()
  • 更适合复杂场景:多个线程执行不同逻辑、带成员变量等。

⚠️ 注意事项

方法 是否直接执行? 是否阻塞主线程?
start() 启动线程(异步)
run() 在主线程执行(同步)
join() 等待线程结束 是(阻塞)

2.2 多线程示例:并发执行任务

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.")

⏱ 输出会交错,说明线程在并发执行。


2.3 给线程传递参数

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()

第 3 章:线程锁防止资源竞争

为什么需要线程锁?

当多个线程访问“共享资源”(如同一个变量、文件、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: 推荐写法,自动加解锁

‍♂️ 第 4 章:守护线程(Daemon Thread)详解

什么是守护线程?

  • 守护线程是一种后台线程,它的生命周期依赖于主线程。
  • 主线程退出时,所有守护线程会自动结束,不管它们是否完成任务。
  • 非守护线程会阻止程序退出,程序必须等待所有非守护线程结束。

如何设置守护线程?

有两种方式:

  1. 线程创建后,调用 setDaemon(True)
  2. 创建时直接传入参数 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 线程未运行完,程序直接退出了。


小结

线程类型 程序退出时行为
非守护线程 程序等待线程结束
守护线程 程序不等待,线程被强制结束

什么时候用守护线程?

  • 你想要线程后台运行,不阻塞程序退出。
  • 线程只做辅助任务(日志、后台清理)。
  • 不需要线程完成所有任务也可以退出。

第 5 章:多线程之间的数据和信号交互

多线程之间如何“对话”?

多个线程如果互不通信,就像工厂里几个工人干着不同的活但互不配合,很容易混乱。线程间的数据交互和信号同步能让它们“协作”工作,提升效率与安全。


常见的线程交互方式

方法 用途 是否线程安全
全局变量 + 锁 手动控制共享数据访问 ✔(加锁)
queue.Queue 自动化数据队列,线程安全
threading.Event 线程间的信号传递

5.1 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()

5.2 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()

5.3 锁(Lock):保护共享资源

多个线程读写同一个变量,可能会造成数据不一致。用 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
多线程同时读写同一变量 LockRLock

小结

  • Queue 最安全,推荐用于线程之间数据传输。
  • Event 适合做线程间的“信号通知”。
  • 锁是最基础的保护机制,但也最容易死锁,小心用!

你可能感兴趣的:(Python笔记,python,linux,网络)