让你的Python并发飞起来:多线程开发实用技巧大全

一、为什么需要多线程?

在日常编程中,单线程程序执行任务时常常会因等待某些操作(比如文件读取、网络请求、数据库等)而"卡住"。多线程可以让你的程序一边处理A业务,一边等待/执行B业务,提升效率、改善用户体验。

常见使用场景:

  • 网络爬虫高并发抓取
  • 显示进度条同时下载/计算
  • 聊天、应用服务端、并发IO操作等

二、多线程的状态以及分类

线程创建之后,并不是始终保持一个状态的,其状态大概如下:

  • New 创建
  • Runnable 就绪。等待调度
  • Running 运行
  • Blocked 阻塞。阻塞可能在 Wait Locked Sleeping
  • Dead 消亡

线程的类型大致分为下面几种:

  • 主线程
  • 子线程
  • 守护线程(后台线程)
  • 前台线程

三、Python如何实现多线程?

Python的多线程主要依靠threading标准库模块。

来几个简单例子快速入门:

1. 最简单的多线程

python 复制代码
import threading

def worker():
    print("线程正在运行", threading.current_thread().name)

t = threading.Thread(target=worker, name='MyThread')
t.start() # 启动线程
t.join() # 等待线程结束

解释:

  • Thread 创建一个线程。
  • start() 启动线程,join() 主线程等待子线程结束。

2. 多线程并发

python 复制代码
import threading
import time

def task(name):
    print(f"{name} 开始工作")
    time.sleep(2)
    print(f"{name} 完成工作")

threads = []
for i in range(3):
    t = threading.Thread(target=task, args=(f'线程{i+1}',))
    t.start()
    threads.append(t)
for t in threads:
    t.join()
print("所有线程结束")

多个线程可同时执行,主线程等待所有线程完结。


四、认识GIL:为什么Python多线程不能提速CPU密集型任务?

GIL(全局解释器锁)是Python CPython解释器的"互斥锁",同一时刻只有一个线程能执行Python字节码。
所以io密集型(爬虫、文件、网络)多线程极其有效,但
CPU密集型任务
常无效(建议多进程)。

CPU密集型程序演示

python 复制代码
import threading
import time

def cpu_task():
    total = 0
    for _ in range(10**7):
        total += 1

threads = [threading.Thread(target=cpu_task) for _ in range(4)]
start = time.time()
for t in threads:
    t.start()
for t in threads:
    t.join()
print("用时:", time.time() - start)

可以试对比用单线程与多线程时间,几乎没有明显提升。


五、共享资源与锁机制

多线程易出现竞态条件问题:多个线程同时操作同一变量时,可能产生不可控的错误。

举例:线程安全导致的数据异常

python 复制代码
import threading

count = 0

def add():
    global count
    for _ in range(100000):
        count += 1

threads = [threading.Thread(target=add) for _ in range(2)]
for t in threads:
    t.start()
for t in threads:
    t.join()
print(count)  # 有很大概率小于200000

解决:加锁(Lock)

python 复制代码
lock = threading.Lock()
count = 0

def safe_add():
    global count
    for _ in range(100000):
        with lock:
            count += 1

threads = [threading.Thread(target=safe_add) for _ in range(2)]
for t in threads:
    t.start()
for t in threads:
    t.join()
print(count)  # 必定是200000

with lock: 能保证同一时刻只有一个线程进入代码块,杜绝数据错乱。


六、更高级:守护线程与条件变量

1. 守护线程(Daemon Thread)

主线程退出时,守护线程自动结束。

python 复制代码
import threading
import time

def worker():
    while True:
        print('工作中...')
        time.sleep(1)

t = threading.Thread(target=worker)
t.daemon = True  # 设置为守护线程,主线程结束时子线程也会退出
t.start()
time.sleep(3)
print("主线程结束")  # 子线程会跟随退出

2. Thread子类实现(面向对象)

python 复制代码
class MyThread(threading.Thread):
    def run(self):
        print("自定义线程运行")

t = MyThread()
t.start()
t.join()

3. 条件变量/信号量(Condition/Semaphore)

用于更复杂的线程同步和通信,可以实现"生产者-消费者"模式、任务队列处理等。

python 复制代码
import threading
import time

condition = threading.Condition()
data = []

def producer():
    for i in range(3):
        with condition:
            data.append(i)
            print("生产:", i)
            condition.notify()
            time.sleep(1)

def consumer():
    for _ in range(3):
        with condition:
            while not data:
                condition.wait()
            print("消费:", data.pop(0))

t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start()
t2.start()
t1.join()
t2.join()

七、典型应用场景案例

1. 多线程爬虫IO密集案例

python 复制代码
import threading
import requests

def fetch(url):
    resp = requests.get(url)
    print(f"{url} 响应长度: {len(resp.text)}")

urls = [
    'https://www.baidu.com',
    'https://www.python.org',
    'https://www.github.com',
]
threads = [threading.Thread(target=fetch, args=(u,)) for u in urls]
for t in threads:
    t.start()
for t in threads:
    t.join()
print("所有网页抓取完成")

大大加快多个网页的抓取速度。

2. 线程池简化并发任务

官方库concurrent.futures.ThreadPoolExecutor推荐用法:

python 复制代码
from concurrent.futures import ThreadPoolExecutor

def square(n):
    return n*n

with ThreadPoolExecutor(max_workers=5) as executor:
    results = list(executor.map(square, range(10)))
print(results)

更方便地发起大量并发任务和收集异步结果。


点个赞,关注我获取更多实用 Python 技术干货!如果觉得有用,记得收藏本文!

相关推荐
我命由我1234510 分钟前
CesiumJS 笔记 - 获取容器中心点、Cartesian3 clone 方法、修改 Cartesian3 对象的高度
前端·javascript·css·前端框架·html·html5·js
从零开始学习人工智能1 小时前
【踩坑实录】WSL2 解决 onnxruntime\-gpu ImportError: libcudart\.so\.13 无 CUDA13 运行库问题
python
Hopebearer_1 小时前
页面突然只剩 DOM?一次静态资源版本错配排查
前端·部署
东风破_2 小时前
ESLint 是什么?为什么你的项目需要它?
前端·后端·代码规范
卷无止境2 小时前
在 awesome-fastapi 里,哪些库值得一看?
后端·python
zhanghaha13143 小时前
Python进阶教程:6_JSON 数据解析 —— 新手完全指南
开发语言·python·json
Python私教3 小时前
API 输出模型怎么设计:从 model_dump() 到显式展示层
python·fastapi
圣殿骑士-Khtangc3 小时前
Go字符串高效拼接性能对比与底层原理分析
服务器·前端·golang
Python私教3 小时前
本地 AI 工具服务该绑定 127.0.0.1 还是 0.0.0.0?
python·fastapi
卷无止境3 小时前
FastAPI 的Admin面板生态
后端·python