(19)100天python从入门到拿捏《多线程》
多线程
文章目录
- 多线程
- 一、什么是多线程
- 二、Python 多线程原理
- 三、创建与启动线程的三种方式
- 方式 1:直接使用 `threading.Thread`
- 方式 2:继承 Thread 类
- 方式 3:使用线程池
- 四、线程同步与锁机制
- 五、线程间通信
- 六、多线程应用场景
- 七、综合案例:多线程爬取网页标题
- 八、总结对比
一、什么是多线程
多线程是在同一个进程中同时运行多个线程的技术。
线程是程序中最小的执行单元,一个进程可以包含多个线程,它们共享同一块内存空间。
可以理解为:
进程是“工厂”,线程是“工人”。
多线程就是让多个“工人”同时工作,提高任务的并发性。
二、Python 多线程原理
Python 提供了多线程支持模块
import threading
但是 —— Python有一个重要的机制叫 GIL全局解释器锁
GIL
- Python在解释器层面同时只能运行一个线程的字节码。
- 因此多线程无法真正实现并行计算**(在 CPU 密集型任务中无效)。
- 但在 IO 密集型任务如网络请求、文件读写中,多线程能显著提高性能。
三、创建与启动线程的三种方式
方式 1:直接使用 threading.Thread
import threading
import timedef 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 timeclass MyThread(threading.Thread):def __init__(self, name):super().__init__()self.name = namedef 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 timedef 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 threadingx = 0def add():global xfor _ in range(1000000):x += 1t1 = threading.Thread(target=add)
t2 = threading.Thread(target=add)
t1.start()
t2.start()
t1.join()
t2.join()print("x =", x) # 理论上应为2000000,但结果往往不对!
加锁后
import threadingx = 0
lock = threading.Lock()def add():global xfor _ in range(1000000):with lock:x += 1t1 = 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 timeq = 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:breakprint("消费:", 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 BeautifulSoupurls = ["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密集 |
典型模块 | threading | multiprocessing |
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从入门到拿捏《网络编程》