我正在研究Ruby WeakRef,而且似乎API的撰寫方式隱含了一個競賽條件,盡管它似乎不太可能發生。
該 API 隱含的基本用法是:
obj = Object.new
foo = WeakRef.new(obj)
# Later on:
if (foo.weakref_alive?)
put "據稱我現在可以使用#{foo.to_s}"
end
# Or even:
obj2 = foo.__getobj__ if foo.weakref_alive?
問題在于我們無法控制何時可能發生垃圾收集,作為一個例子,考慮另一個運行中的執行緒,它定期呼叫GC.start。
如果我們在weakref_alive?檢查和物件的使用之間發生了垃圾收集,那么我們將最終遇到RefError例外。
(實際上,我預計任何使用弱ref的大型應用程式--尤其是那些多執行緒的應用程式--都會因為這個原因而偶爾遇到RefErrors)
如果物件在我們檢查它的時候是可用的,我很驚訝沒有辦法以原子方式安全地獲得物件。
因此問題是,首先,我是否過于關注? 如果我們在檢查完物件后立即獲取該物件(如第二個例子),我們是否有理由不必永遠擔心 GC 的發生? 如果不是的話,那么我們就會遇到第二個問題,即安全使用弱refs的最佳方式。
現在,我已經為該類添加了一個 "obj "方法,作為處理該問題的一種方式:
現在,我已經為該類添加了一個 "obj "方法。
require '弱ref'
class WeakRef
def obj
begin>
return self.__getobj__
rescue RefError
return nil rescue RefError
結束
end end
end end
但是不必要的 "救援 "陳述句讓我很不爽。 我想我們也可以:
require '弱ref'
class WeakRef
def obj
savegc = GC.disable
obj = self.weakref_alive? ? self.__getobj__ : nil.
GC.enable if savegc
return obj
結束
end
但我對只是禁用和重新啟用垃圾收集的成本是否很低持懷疑態度,更不用說這是否是一個完全的原子操作。
Ruby GC專家有什么建議嗎?
uj5u.com熱心網友回復:
首先,請注意WeakRef物件的預期用途,即代替原始物件。在這里,WeakRef物件通過轉發所有發送到它的訊息來實作被參考物件的全部鴨式介面。因此,WeakRef物件旨在被直接用來替代原始物件(如果它仍然可用的話)。
盡管您可以通過WeakRef#__getobj__來獲得對原始物件的參考(如果它仍然可用的話),但這只是一個特殊的使用情況,更多的是訊息委托的一個實作細節。然而,如果你這樣做,你可以用WeakRef#weakref_alive?檢查被參考的物件是否仍然可用。正如您所注意到的,根據您所使用的 Ruby 實作,存在著(至少是理論上的)競賽條件的選項。
為了確保您能夠優雅地處理此類競賽條件,您確實可以在RefError發生時進行救援。你只需對非競賽條件的情況稍作優化:
obj = Object.new
foo = WeakRef.new(obj)
begin
obj2 = foo.__getobj__ if foo.weakref_alive?
rescue RefError
obj2 = nil rescue RefError.
結束
你可以對發送到你的弱參考的任何其他訊息使用同樣的模式(然后被forwarrdr到你的參考物件),例如:
begin
foo.to_s if foo.weakref_alive?
rescue RefError
# do nothing as foo is a dangling reference to a garbag-collected object[/span]。
end
根據你的使用情況,這可能有點尷尬。此外,有時有必要獲得實際的物件參考,而不是被包裝的物件(當查詢其特定的類時,可能會有不同的表現,例如在case陳述句中)。
在這里,一種選擇可以是使用ObjectSpace::WeakMap而不是WeakRef。這個類在內部被WeakRef使用,以實際持有弱參考。Ruby實際上不鼓勵使用這個類,并將其視為一個內部類。然而,我發現它對于實作一個更直接的查找是很有用的,而不是僅僅使用WeakRef。請注意,這一領域的行為可能會發生微妙的變化,當您更新您的 Ruby 版本時,閱讀更改日志可能是一個好主意。
既然如此,使用ObjectSpace::WeakMap的查找示例可能看起來像這樣:
# WeakMap物件,它可以從一個存盤多個地圖。
# 現有物件到另一個(可能是垃圾收集的)物件。
# 如果你需要多個弱參考,你仍然可以使用
# 一個單一的map。
WEAK_MAP = ObjectSpace::WeakMap.new
# 我們的參考物件可能會或可能不會被垃圾回收 # 我們的參考物件可能會或可能不會被垃圾回收
obj = Object.new
# "marker "物件是map中的關鍵。它被用來查找參考。
# 到目標物件。你需要在這里始終使用同一個物件。
#(而不是例如一個類似的字串)作為標記的實際object_id。
#用于查找被參考的物件。
marker = Object.new
# 在弱映射中存盤參考
WEAK_MAP[marker] = obj
#########################################################
# Now do something else... #
# obj可能會在這期間被收集到垃圾。 #
# You need to hold onto the marker object though! #
#########################################################
# 現在,你可以檢索到對實際的參考。
# 原始物件(如果它仍然可用)或nil的參考。
# if obj was already garbag-collected[/span].
obj2 = WEAK_MAP[marker] 。
如上所述,WeakRef類內部正是使用了這種機制。在這里,WeakRef物件使用自己作為標記。也就是說,只要你持有實際的WeakRef物件。因此,在WeakRef#__getobj__中的簡化查找看起來是這樣的:
class WeakRef>
WEAK_MAP = ObjectSpace::WeakMap.new
def __getobj__
WEAK_MAP[self] || raise RefError, "Invalid Reference"
end
def weakref_alive?
!WEAK_MAP[self].nil?
# 實際上,這主要是等價的代碼。
# WEAK_MAP.key?(self)
end
end。
你可以在https://github.com/ruby/ruby/blob/master/lib/weakref.rb找到WeakRef類的實作--看一看,它實際上是相當可讀的。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/307592.html
標籤:
