python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python多线程编程

Python多线程编程的核心概念与实践方法

作者:detayun

线程(Thread)是操作系统能够进行运算调度的最小单位,多线程就是在一个程序中同时运行多个线程,让它们"看起来"在同时执行不同的任务,本文介绍了Python多线程编程的核心概念与实践方法,感兴趣的小伙伴可以了解下

一、什么是多线程?

线程(Thread)是操作系统能够进行运算调度的最小单位。一个进程可以包含多个线程,这些线程共享进程的资源(如内存、文件句柄等),但每个线程有自己独立的执行栈和程序计数器。

多线程就是在一个程序中同时运行多个线程,让它们"看起来"在同时执行不同的任务。

二、为什么要用多线程

1. 提升I/O密集型任务的效率

当程序需要等待网络请求、数据库查询、文件读写等I/O操作时,单线程会一直"傻等"。而多线程可以让一个线程等待时,另一个线程继续执行其他任务。

举例

2. 提高用户体验

在GUI程序中,如果主线程被耗时操作阻塞,界面会"假死"。用多线程可以让后台任务运行,界面保持响应。

三、Python中的多线程模块

Python提供了两个主要的多线程模块:

模块说明适用场景
threading高级模块,基于线程通用多线程编程
_thread低级模块(很少直接用)底层控制

四、快速上手:创建线程

方法1:使用threading.Thread

import threading
import time

def task(name, seconds):
    print(f"线程 {name} 开始执行")
    time.sleep(seconds)
    print(f"线程 {name} 执行完毕")

# 创建线程
t1 = threading.Thread(target=task, args=("A", 2))
t2 = threading.Thread(target=task, args=("B", 1))

# 启动线程
t1.start()
t2.start()

# 等待线程结束
t1.join()
t2.join()

print("所有线程执行完毕")

输出

线程 A 开始执行
线程 B 开始执行
线程 B 执行完毕
线程 A 执行完毕
所有线程执行完毕

方法2:继承threading.Thread类

import threading
import time

class MyThread(threading.Thread):
    def __init__(self, name):
        super().__init__()
        self.name = name
    
    def run(self):
        print(f"线程 {self.name} 开始")
        time.sleep(2)
        print(f"线程 {self.name} 结束")

t = MyThread("自定义线程")
t.start()
t.join()

五、线程 vs 进程

特性线程(Thread)进程(Process)
创建开销
内存共享共享进程内存独立内存空间
切换速度
GIL限制受GIL影响(CPU密集型无效)不受GIL影响
适用场景I/O密集型CPU密集型

六、GIL(全局解释器锁)是什么?

GIL(Global Interpreter Lock) 是Python的一个机制,它保证同一时刻只有一个线程在执行Python字节码。

影响

解决方案

七、线程同步:锁(Lock)

当多个线程同时修改共享数据时,可能会出现数据不一致的问题。这时需要用来保证线程安全。

import threading

balance = 0
lock = threading.Lock()

def deposit():
    global balance
    for _ in range(100000):
        lock.acquire()  # 加锁
        try:
            balance += 1
        finally:
            lock.release()  # 解锁

t1 = threading.Thread(target=deposit)
t2 = threading.Thread(target=deposit)

t1.start()
t2.start()
t1.join()
t2.join()

print(f"最终余额: {balance}")  # 正确输出 200000

不加锁的后果

# 去掉 lock,结果可能是 199847(数据不一致)

八、线程池:管理线程的最佳实践

手动创建和销毁线程效率低,推荐使用线程池

使用ThreadPoolExecutor

from concurrent.futures import ThreadPoolExecutor
import time

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

# 创建线程池(最多5个线程)
with ThreadPoolExecutor(max_workers=5) as executor:
    # 提交10个任务
    futures = [executor.submit(task, i) for i in range(10)]
    
    # 获取结果
    for future in futures:
        print(future.result())

优势

九、实际案例:批量下载图片

import threading
import requests
from concurrent.futures import ThreadPoolExecutor

def download_image(url):
    try:
        response = requests.get(url, timeout=10)
        filename = url.split("/")[-1]
        with open(filename, "wb") as f:
            f.write(response.content)
        print(f"✅ 下载完成: {filename}")
    except Exception as e:
        print(f"❌ 下载失败: {url}, 错误: {e}")

urls = [
    "https://example.com/image1.jpg",
    "https://example.com/image2.jpg",
    "https://example.com/image3.jpg",
]

# 使用线程池并发下载
with ThreadPoolExecutor(max_workers=3) as executor:
    executor.map(download_image, urls)

十、常见错误及解决方案

错误原因解决方案
RuntimeError: can't start new thread线程数超过系统限制使用线程池,限制并发数
数据不一致多线程同时修改共享数据使用 Lock
程序假死主线程被阻塞将耗时任务放到子线程
CPU密集型任务变慢GIL限制改用 multiprocessing 多进程

十一、最佳实践总结

  1. 优先使用线程池ThreadPoolExecutor),不要手动创建线程
  2. I/O密集型任务用多线程,CPU密集型用多进程
  3. 共享数据必须加锁threading.Lock
  4. 设置合理的线程数(一般不超过 CPU核心数 × 2)
  5. 避免在子线程中操作GUI(会崩溃)
  6. 不要过度创建线程(会耗尽系统资源)

十二、进阶学习路线

阶段内容
入门threading.ThreadLockThreadPoolExecutor
进阶queue.Queue(线程间通信)、EventCondition
高级asyncio 异步编程、multiprocessing 多进程
实战爬虫并发、Web服务器、GUI后台任务

总结

Python多线程是处理I/O密集型任务的利器,但要注意GIL的限制和线程安全问题。记住:用线程池代替手动管理线程,用锁保护共享数据,用多进程替代CPU密集型任务。

到此这篇关于Python多线程编程的核心概念与实践方法的文章就介绍到这了,更多相关Python多线程编程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文