文章源地址:神奇的弱引用 | 小菜学编程
背景
开始讨论弱引用( weakref )之前,我们先来看看什么是弱引用?它到底有什么作用?
假设我们有一个多线程程序,并发处理应用数据:
# 占用大量资源,创建销毁成本很高 class Data: def __init__(self, key): pass
应用数据 Data 由一个 key 唯一标识,同一个数据可能被多个线程同时访问。由于 Data 需要占用很多系统资源,创建和消费的成本很高。我们希望 Data 在程序中只维护一个副本,就算被多个线程同时访问,也不想重复创建。
为此,我们尝试设计一个缓存中间件 Cacher :
import threading
# 数据缓存
class Cacher:
def __init__(self):
self.pool = {}
self.lock = threading.Lock()
def get(self, key):
with self.lock:
data = self.pool.get(key)
if data:
return data
self.pool[key] = data = Data(key)
return data
Cacher 内部用一个 dict 对象来缓存已创建的 Data 副本,并提供 get 方法用于获取应用数据 Data 。get 方法获取数据时先查缓存字典,如果数据已存在,便直接将其返回;如果数据不存在,则创建一个并保存到字典中。因此,数据首次被创建后就进入缓存字典,后续如有其它线程同时访问,使用的都是缓存中的同一个副本。
感觉非常不错!但美中不足的是:Cacher 有资源泄露的风险!
因为 Data 一旦被创建后,就保存在缓存字典中,永远都不会释放!换句话讲,程序的资源比如内存,会不断地增长,最终很有可能会爆掉。因此,我们希望一个数据等所有线程都不再访问后,能够自动释放。
我们可以在 Cacher 中维护数据的引用次数, get 方法自动累加这个计数。于此同时提供一个 remove 新方法用于释放数据,它先自减引用次数,并在引用次数降为零时将数据从缓存字段中删除。
线程调用 get 方法获取数据,数据用完后需要调用 remove 方法将其释放。Cacher 相当于自己也实现了一遍引用计数法,这也太麻烦了吧!Python 不是内置了垃圾回收机制吗?为什么应用程序还需要自行实现呢?
冲突的主要症结在于 Cacher 的缓存字典:它作为一个中间件,本身并不使用数据对象,因此理论上不应该对数据产生引用。那有什么黑科技能够在不产生引用的前提下,找到目标对象吗?我们知道,赋值都是会产生引用的!
典型用法
这时,弱引用( weakref )隆重登场了!弱引用是一种特殊的对象,能够在不产生引用的前提下,关联目标对象。
# 创建一个数据
>>> d = Data('fasionchan.com')
>>> d
<__main__.Data object at 0x1018571f0>
# 创建一个指向该数据的弱引用
>>> import weakref
>>> r = weakref.ref(d)
# 调用弱引用

本文探讨了Python中的弱引用,如何解决资源泄露问题。弱引用允许在不增加引用计数的情况下关联对象,当所有强引用消失后,对象会被自动释放。通过弱引用对象的`wr_object`字段和观察者模式实现这一机制。弱引用适用于缓存中间件,如`WeakKeyDictionary`和`WeakValueDictionary`,确保数据在无引用时自动清理。
最低0.47元/天 解锁文章
1272

被折叠的 条评论
为什么被折叠?



