多线程

一、什么是多线程

多线程是在同一个进程中同时运行多个线程的技术。
线程是程序中最小的执行单元,一个进程可以包含多个线程,它们共享同一块内存空间。

可以理解为:

进程是“工厂”,线程是“工人”。
多线程就是让多个“工人”同时工作,提高任务的并发性。

二、Python 多线程原理

Python 提供了多线程支持模块

import threading

但是 —— Python有一个重要的机制叫 GIL全局解释器锁

GIL

  • Python在解释器层面同时只能运行一个线程的字节码。
  • 因此多线程无法真正实现并行计算**(在 CPU 密集型任务中无效)。
  • 但在 IO 密集型任务如网络请求、文件读写中,多线程能显著提高性能。

三、创建与启动线程的三种方式

方式 1:直接使用 threading.Thread

import threading
import time

def worker(name):
    print(f"线程 {name} 开始工作")
    time.sleep(2)
    print(f"线程 {name} 结束工作")

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

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

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

print("所有线程执行完毕")
线程 A 开始工作
线程 B 开始工作
线程 A 结束工作
线程 B 结束工作
所有线程执行完毕

说明:

  • target:要执行的函数名
  • args:传递给函数的参数(必须是元组)
  • start():启动线程
  • join():等待线程执行完成

方式 2:继承 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(1)
        print(f"线程 {self.name} 结束工作")

# 创建并启动线程
threads = [MyThread(f"Worker-{i}") for i in range(3)]
for t in threads:
    t.start()
for t in threads:
    t.join()
线程 Worker-0 开始工作
线程 Worker-1 开始工作
线程 Worker-2 开始工作
线程 Worker-0 结束工作
线程 Worker-2 结束工作
线程 Worker-1 结束工作

适合需要重写逻辑的复杂任务。


方式 3:使用线程池

from concurrent.futures import ThreadPoolExecutor
import time

def task(n):
    print(f"执行任务 {n}")
    time.sleep(1)
    return f"结果 {n}"

# 创建线程池
with ThreadPoolExecutor(max_workers=3) as executor:
    results = executor.map(task, range(5))

for r in results:
    print(r)
执行任务 0
执行任务 1
执行任务 2
执行任务 3
执行任务 4
结果 0
结果 1
结果 2
结果 3
结果 4

线程池优势:

  • 自动管理线程创建与回收;
  • 支持任务提交、结果收集;
  • 推荐现代项目使用。

四、线程同步与锁机制

由于多个线程共享同一块内存空间,可能同时修改同一变量,造成数据错误。因此需要使用 Lock锁 保护共享资源。

案例:未加锁时数据错误

import threading

x = 0

def add():
    global x
    for _ in range(1000000):
        x += 1

t1 = threading.Thread(target=add)
t2 = threading.Thread(target=add)
t1.start()
t2.start()
t1.join()
t2.join()

print("x =", x)  # 理论上应为2000000,但结果往往不对!

加锁后

import threading

x = 0
lock = threading.Lock()

def add():
    global x
    for _ in range(1000000):
        with lock:
            x += 1

t1 = threading.Thread(target=add)
t2 = threading.Thread(target=add)
t1.start()
t2.start()
t1.join()
t2.join()

print("x =", x)  # 结果正确:2000000

五、线程间通信

线程之间不能直接共享变量数据时,可以使用 queue.Queue 实现线程安全通信。

import threading
import queue
import time

q = queue.Queue()

def producer():
    for i in range(5):
        item = f"任务{i}"
        print("生产:", item)
        q.put(item)
        time.sleep(1)

def consumer():
    while True:
        item = q.get()
        if item is None:
            break
        print("消费:", item)
        time.sleep(2)

# 创建线程
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)

t1.start()
t2.start()

t1.join()
q.put(None)  # 发送结束信号
t2.join()
生产: 任务0
消费: 任务0
生产: 任务1
消费: 任务1
生产: 任务2
生产: 任务3
消费: 任务2
生产: 任务4
消费: 任务3
消费: 任务4

六、多线程应用场景

场景例子是否推荐
IO密集型文件读写、网络请求、数据库操作✅ 推荐使用
CPU密集型数学计算、图像处理、深度学习❌ 建议使用多进程
并发爬虫同时抓取多个网页✅ 非常常用
实时数据流处理例如日志监听、Socket通信✅ 适用

七、综合案例:多线程爬取网页标题

import threading
import requests
from bs4 import BeautifulSoup

urls = [
    "https://www.python.org/",
    "https://docs.python.org/3/",
    "https://pypi.org/",
]

def fetch_title(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.text, "html.parser")
    print(f"{url} 标题:{soup.title.string}")

threads = []
for url in urls:
    t = threading.Thread(target=fetch_title, args=(url,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

print("所有网页爬取完毕。")

八、总结对比

特性多线程多进程
共享内存✅❌
创建开销小大
适用任务IO密集CPU密集
典型模块threadingmultiprocessing

python学习专栏导航
(1)100天python从入门到拿捏《Python 3简介》
(2)100天python从入门到拿捏《python应用前景》
(3)100天python从入门到拿捏《数据类型》
(4)100天python从入门到拿捏《运算符》
(5)100天python从入门到拿捏《流程控制语句》
(6)100天python从入门到拿捏《推导式》
(7)100天python从入门到拿捏《迭代器和生成器》
(8)100天python从入门到拿捏《函数和匿名函数》
(9)100天python从入门到拿捏《装饰器》
(10)100天python从入门到拿捏《Python中的数据结构与自定义数据结构》
(11)100天python从入门到拿捏《模块》
(12)100天python从入门到拿捏《文件操作》
(13)100天python从入门到拿捏《目录操作》
(14)100天python从入门到拿捏《Python的错误与异常机制》
(15)100天python从入门到拿捏《面向对象编程》
(16)100天python从入门到拿捏《标准库》
(17)100天python从入门到拿捏《正则表达式》
(18)100天python从入门到拿捏《网络编程》

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐