Ruby WeakRef是否存在隐式竞态条件?如何安全使用?
关于Ruby WeakRef隐式竞态问题的解答
问题1:该竞态条件真实存在,你没有过度担忧
官方给出的先调用weakref_alive?检查、再使用对象的写法存在天然的时间差窗口,Ruby没有任何机制保证该窗口内不会触发GC。不管你检查后调用对象的速度有多快,只要GC在这个间隙执行、且此时没有其他强引用指向原对象,原对象就会被回收,后续调用必然抛出RefError。多线程场景下这个问题的出现概率会进一步提升,完全不是极端小概率问题,长期运行的大型应用尤其是多线程应用,必然会偶现该异常。
问题2:安全操作WeakRef的最优方案
你给出的两种自定义实现的优劣非常明确:
- 第一种基于
rescue捕获RefError的实现,是目前最推荐的生产级方案。很多开发者对异常捕获有性能顾虑,但实际上Ruby的异常开销仅在异常实际触发时才会产生,而弱引用对象被回收属于极少数场景,正常访问路径下的性能损耗可以忽略不计。而且__getobj__方法本身是原子操作,底层实现会在单次操作内完成存活校验+对象返回,不存在GC介入的窗口,完全规避了竞态问题。 - 第二种禁用GC的实现虽然能解决竞态,但副作用极大:频繁开关GC会干扰Ruby的正常垃圾回收调度,高并发场景下可能导致内存占用暴涨、整体性能下降,而且如果方法执行中出现未捕获的异常导致
GC.enable没有执行,会造成整个进程的GC永久停掉,风险极高,完全不推荐使用。
你还可以进一步精简写法,weakref_alive?检查本身就是多余的,直接调用__getobj__配合异常捕获即可,参考实现如下:
require 'weakref' class WeakRef # 安全获取原对象,对象已回收则返回nil def safe_get __getobj__ rescue WeakRef::RefError nil end end
使用时直接拿返回值判断即可,拿到的返回值已经是强引用,GC不会回收,可以安全使用:
obj = foo.safe_get if obj puts "安全获取到对象:#{obj}" end
内容的提问来源于stack exchange,提问作者David Ljung Madison Stellar
相关产品推荐
相关产品推荐

