背景
在开始讨论弱引用的实现之前,我们先来看看什么是弱引用以及弱引用的作用。
在我们平常编写代码的时候我们经常会使用引用,了解一下Python,我们就会知道,当一个对象的引用为0时,才会销毁该对象。
那么比如我们现在需要维护这样一个东西:
不断向一个多线程程序发送数据,同时引入一个缓存来存储数据,但是当没有人使用该数据的时候,销毁这个缓存来节省空间。
那么一个很显然的思路是建立一个 dict,然后不断往 dict 里面添加信息,这时候我们就会注意到这样的问题:dict 存储的信息都是引用,当我们使用完了数据,还是会留下一个dict中的引用,这样数据并不会被销毁,而且会越堆积越多。
示例代码如下:
import threading
class Data:
def __init__(self, key):
pass
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
如何解决引用带来的内存占用
那么这个时候就要用到我们的弱引用了。
弱引用并不会增加引用数,但是它会关联目标对象。
如图所示
当我们删除引用时,就会变成如下情况
这样一来,我们只需要把缓存改成弱引用即可。
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
data = Data(key)
self.pool[key] = weakref.ref(data)
return data
由于缓存 dict 只保存 Data 对象的弱引用,因此 Cacher 不会影响 Data 对象的引用计数,当所有线程用完之后,引用计数就归零,数据就会被释放。
实际上用字典缓存数据对象很常见,所以weakref模块还提供了两种只保存弱引用的字典对象:
weakref.WeakKeyDictionary
键只保存弱引用weakref.WeakValueDictionary
值只保存弱引用
所以我们也可以通过把 dict 改成 WeakValueDictionary 来实现我们想要的功能
class Cacher:
def __init__(self):
self.pool = weakref.WeakValueDictionary()
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
工作原理
然后我们就可以去阅读源代码了,笔者这里直接找到了 CPython 实现
注意:3.11以后的代码中的单独有一个头文件来做 typedef,读者不必纠结 PyObecjt 从哪里来
笔者所看的为3.11 版本号c8de883bcb022b59d7ae6d20993599b47e4b968b
在 /Objects/weakrefobject.c 和 /Include/weakrefobject.h 中
很容易就可以发现 PyWeakReference 包含如下字段:
我们主要解释 wr_object
, wr_callback
, hash
, wr_prev
和 wr_next
wr_object
是对象指针,指向被引用的对象,弱引用根据该字段找到被引用的对象,但是不会产生引用。wr_callback
指向一个可调用对象,当被引用的对象销毁时调用hash
缓存被引用对象的哈希值wr_prev
和wr_next
分别是前后向指针,用于将弱引用对象组织成双向链表。
所以一个对象的弱引用组织结构如下
- 弱引用对象通过
wr_object
字段来关联被引用对象,就像上面图中红色箭头一样 - 一个对象可以同时被多个弱引用对象关联,图中的 Data 实例被两个弱引用对象关联 weakrefobject.c 943行
- 当一个对象被销毁了之后,Python将遍历一遍弱引用链表,逐一处理。
- 将
wr_object
设置为None
具体是现在 weakrefobject.c 中第55行 - 执行回调函数
wr_callback
(如果有)
- 将
由此可见,弱引用的工作原理就是设计模式中的 观察者模式。当对象被销毁,所有的弱引用对象都得到通知并进行处理。
一些实现细节后面更新……
标签:Python,self,Weakref,对象,引用,key,data,wr From: https://www.cnblogs.com/Gensokyo-Alice/p/17742717.html