Python线程全面详解:从基础概念到高级应用

一、线程基础概念

1.1 进程与线程的关系

进程是操作系统资源分配的基本单位,它是程序的一次执行过程。当我们将程序加载到内存中运行时,系统会为它分配CPU、内存、文件句柄等资源,这时就形成了一个进程。

线程是CPU调度的基本单位,它是进程中的一个执行流。一个进程可以包含多个线程,这些线程共享进程的资源,但每个线程有自己的执行路径和栈空间。

关键区别

  • 进程间相互独立,线程间共享进程资源

  • 进程切换开销大,线程切换开销小

  • 进程通信需要IPC机制,线程可直接读写进程数据段

1.2 为什么需要线程

虽然进程已经实现了多道编程,但仍存在两个主要缺陷:

  1. 单任务限制:传统进程一次只能执行一个任务

  2. 阻塞问题:进程某部分阻塞会导致整个进程挂起

现实类比:将上课看作一个进程,我们需要同时:

  • 耳朵听老师讲课(线程1)

  • 手上记笔记(线程2)

  • 脑子思考问题(线程3)

如果没有线程机制,这三件事只能顺序执行,效率低下。

二、Python线程实现

2.1 线程模块选择

Python提供了多个线程相关模块:

  • thread:基础线程模块(已过时,不推荐)

  • threading:高级线程接口(推荐使用)

  • Queue:线程安全队列实现

2.2 创建线程的两种方式

方式一:使用Thread类直接创建
from threading import Thread
import time

def task(num):
    time.sleep(0.5)
    print(f"线程{num}执行")

if __name__ == '__main__':
    threads = []
    for i in range(3):
        t = Thread(target=task, args=(i,))
        threads.append(t)
        t.start()
    
    for t in threads:
        t.join()  # 等待所有线程完成
方式二:继承Thread类创建
from threading import Thread

class MyThread(Thread):
    def __init__(self, num):
        super().__init__()
        self.num = num
        
    def run(self):
        print(f"自定义线程{self.num}执行")

if __name__ == '__main__':
    t = MyThread(1)
    t.start()
    t.join()

2.3 Thread类常用方法

方法名描述
start()启动线程
join([timeout])等待线程终止
is_alive()返回线程是否存活
name线程名称
ident线程标识符
daemon是否为守护线程

2.4 threading模块常用函数

函数描述
threading.current_thread()返回当前线程对象
threading.active_count()当前活跃线程数
threading.enumerate()返回所有活跃线程列表
threading.main_thread()返回主线程对象

三、线程与进程性能对比

3.1 创建开销对比

from threading import Thread
from multiprocessing import Process
import time

def task(num):
    pass

# 进程测试
start = time.time()
processes = []
for i in range(100):
    p = Process(target=task, args=(i,))
    p.start()
    processes.append(p)
    
[p.join() for p in processes]
print("多进程耗时:", time.time()-start)  # 约11秒

# 线程测试
start = time.time()
threads = []
for i in range(100):
    t = Thread(target=task, args=(i,))
    t.start()
    threads.append(t)
    
[t.join() for t in threads]
print("多线程耗时:", time.time()-start)  # 约0.02秒

3.2 PID对比

import os
from threading import Thread
from multiprocessing import Process

def show_pid(label):
    print(f"{label} PID:", os.getpid())

# 进程
p = Process(target=show_pid, args=("进程",))
p.start()  # 显示不同PID

# 线程
t = Thread(target=show_pid, args=("线程",))
t.start()  # 显示相同PID

四、守护线程详解

4.1 守护线程 vs 守护进程

守护进程特点

  1. 随主进程结束而立即结束

  2. 主进程会等待非守护子进程完成

  3. 守护进程主要用于服务主进程

守护线程特点

  1. 随主线程结束而结束(实际是进程内所有非守护线程结束后)

  2. 主线程会等待所有非守护线程完成

  3. 守护线程通常用于后台支持任务

4.2 代码示例

from threading import Thread
import time

def daemon_task():
    print("守护线程开始")
    time.sleep(5)
    print("守护线程结束")  # 可能不会执行

def normal_task():
    print("普通线程开始")
    time.sleep(2)
    print("普通线程结束")

if __name__ == '__main__':
    d = Thread(target=daemon_task)
    d.daemon = True
    
    n = Thread(target=normal_task)
    
    d.start()
    n.start()
    
    print("主线程结束")
    # 程序会在普通线程结束后退出,守护线程可能被强制结束

五、线程同步机制

5.1 互斥锁(Lock)

from threading import Thread, Lock

counter = 0
lock = Lock()

def increment():
    global counter
    for _ in range(100000):
        lock.acquire()
        counter += 1
        lock.release()

threads = []
for i in range(5):
    t = Thread(target=increment)
    threads.append(t)
    t.start()

for t in threads:
    t.join()

print("Final counter:", counter)  # 正确结果500000

5.2 死锁问题与解决方案

死锁示例

from threading import Thread, Lock

lock1 = Lock()
lock2 = Lock()

def func1():
    lock1.acquire()
    print("Func1获取lock1")
    lock2.acquire()
    print("Func1获取lock2")
    lock2.release()
    lock1.release()

def func2():
    lock2.acquire()
    print("Func2获取lock2")
    lock1.acquire()
    print("Func2获取lock1")
    lock1.release()
    lock2.release()

t1 = Thread(target=func1)
t2 = Thread(target=func2)
t1.start()
t2.start()
t1.join()
t2.join()

解决方案:使用RLock(可重入锁)

from threading import RLock

lock = RLock()

def recursive_func(n):
    if n > 0:
        lock.acquire()
        print(f"获取锁,n={n}")
        recursive_func(n-1)
        lock.release()

recursive_func(3)

5.3 信号量(Semaphore)

from threading import Semaphore, Thread
import time

sem = Semaphore(3)  # 允许最多3个线程同时访问

def task(name):
    print(f"{name} 等待获取信号量")
    sem.acquire()
    print(f"{name} 获取了信号量")
    time.sleep(2)
    sem.release()
    print(f"{name} 释放了信号量")

for i in range(10):
    t = Thread(target=task, args=(f"Thread-{i}",))
    t.start()

5.4 事件(Event)

from threading import Event, Thread
import time

event = Event()

def waiter():
    print("等待事件触发")
    event.wait()  # 阻塞直到事件被设置
    print("事件已触发,继续执行")

def setter():
    time.sleep(3)
    print("设置事件")
    event.set()  # 触发事件

Thread(target=waiter).start()
Thread(target=setter).start()

5.5 条件变量(Condition)

from threading import Condition, Thread

condition = Condition()
items = []

def consumer():
    condition.acquire()
    if not items:
        print("消费者等待...")
        condition.wait()  # 释放锁并等待
    print(f"消费物品: {items.pop()}")
    condition.release()

def producer():
    condition.acquire()
    items.append("新产品")
    print("生产者通知...")
    condition.notify()  # 唤醒一个等待线程
    condition.release()

Thread(target=consumer).start()
Thread(target=producer).start()

六、线程池与高级用法

6.1 使用ThreadPoolExecutor

from concurrent.futures import ThreadPoolExecutor
import time

def task(name):
    print(f"任务 {name} 开始")
    time.sleep(2)
    return f"任务 {name} 完成"

with ThreadPoolExecutor(max_workers=3) as executor:
    futures = [executor.submit(task, i) for i in range(5)]
    for future in futures:
        print(future.result())  # 获取任务结果

6.2 线程局部数据

from threading import Thread, local

thread_data = local()
thread_data.x = 0

def task():
    thread_data.x = 1
    print(f"子线程: {thread_data.x}")

t = Thread(target=task)
t.start()
t.join()

print(f"主线程: {thread_data.x}")  # 仍然是0,各线程独立

七、线程编程最佳实践

  1. 避免全局变量:尽量使用参数传递数据

  2. 合理使用锁:锁的范围要尽可能小

  3. 防止死锁:按固定顺序获取多个锁

  4. 优先使用队列:Queue是线程安全的通信方式

  5. 考虑GIL影响:CPU密集型任务考虑多进程

  6. 资源清理:确保线程结束时释放资源

  7. 异常处理:线程内异常不会传播到主线程

八、常见问题解答

Q:Python多线程真的能提高性能吗?
A:对于I/O密集型任务,多线程能显著提高性能;对于CPU密集型任务,由于GIL的存在,多线程可能不会提高性能,此时应考虑多进程。

Q:如何选择多线程还是多进程?
A:根据任务类型选择:

  • I/O密集型:多线程

  • CPU密集型:多进程

  • 混合型:多进程+多线程

Q:线程安全的数据结构有哪些?
A:Python中的Queue、deque(需加锁)、collections.defaultdict(需加锁)等,或使用threading.local实现线程局部存储。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值