2

如下所示,在 中twisted.spread.flavors.RemoteCache.unjellyFor,我们创建了一个名为的虚拟对象cProxy并将其返回给客户端代码的其余部分,而不是返回self

def unjellyFor(self, unjellier, jellyList):
    if unjellier.invoker is None:
        return setInstanceState(self, unjellier, jellyList)
    self.broker = unjellier.invoker
    self.luid = jellyList[1]
    cProxy = _newDummyLike(self)
    # XXX questionable whether this was a good design idea...
    init = getattr(cProxy, "__init__", None)
    if init:
        init()
    unjellier.invoker.cacheLocally(jellyList[1], self)
    cProxy.setCopyableState(unjellier.unjelly(jellyList[2]))
    # Might have changed due to setCopyableState method; we'll assume that
    # it's bad form to do so afterwards.
    self.__dict__ = cProxy.__dict__
    # chomp, chomp -- some existing code uses "self.__dict__ =", some uses
    # "__dict__.update".  This is here in order to handle both cases.
    self.broker = unjellier.invoker
    self.luid = jellyList[1]
    return cProxy

_newDummyLike 的主体如下所示:

def _newDummyLike(instance):
    """
    Create a new instance like C{instance}.

    The new instance has the same class and instance dictionary as the given
    instance.

    @return: The new instance.
    """
    if isinstance(instance.__class__, type):
        # New-style class
        dummy = _DummyNewStyle()
    else:
        # Classic class
        dummy = _Dummy()
    dummy.__class__ = instance.__class__
    dummy.__dict__ = instance.__dict__
    return dummy

由于虚拟对象与“真实”对象cProxy共享它的__dict____class__,因此我根本看不出制作虚拟对象的意义。为什么要创建假人?

4

2 回答 2

2

这些“虚拟”对象的目的是分布式垃圾收集。

首先,让我们考虑 a 的简单情况Copyable。每次序列化它时,你的对等点都会得到一个新的RemoteCopy. 简单 - 没有什么可跟踪的。您Copyable可以随时轻松收集垃圾。

接下来,Referenceable。每次序列化它时,你的对等点都会得到一个新的RemoteReference. 现在我们有一个问题:如果你的同行仍然有那个RemoteReference,他们应该能够调用你的方法Referenceable,这意味着你的经纪人现在拥有对你的强引用Referenceable。有点棘手,但仍然相当简单:每次 aRemoteReference被垃圾收集时RemoteReference.__del__,我们都会发送一条decref消息,告诉发送者它们Referenceable不再被引用。当计数为零时,强引用可能会被淘汰,自然会被垃圾回收。这是有效的,因为 aRemoteReference实际上是不可变的——它只包含相关对象的不透明标识符、对代理的引用,仅此而已。

最后,Cacheable。在这里,我们遇到了一个真正的难题。您序列化您的Cacheable,现在Broker需要保持对 的强引用Cacheable,以便能够判断它是否相同Cacheable,稍后再次发送。但在线路的另一端,如果隐式引用正在消失RemoteCache,需要__del__通知我们的- 也有来自代理的强引用,因为可能需要将更新发送到.CacheableCacheableRemoteCache

这是一个循环引用,也是一个糟糕的引用。我们需要一些方法来打破循环,以便我们交给应用程序的东西,它会被垃圾收集,每次服务器向我们发送不同的RemoteCache. 事实证明,我们可以__del__做到这一点,并打破循环引用(请记住,这段代码早于 python 的 GC!),方法是为每个RemoteCache发送的每个实例对象生成一个单独的实例对象,每个实例对象都有自己的方法。但是我们可以通过提供共享其内部状态的应用程序对象来保持一致性的错觉这样,当__dict____del__RemoteCache对象被垃圾收集,它们每个都发送一条消息,我们将“真实”的(self在您的示例中)与来自代理的强引用一起保留,它成为“zeroeth”引用,这也是它第一次被复制的原因(您可以看到只有一个其他调用站点,_newDummyLike其中对相同可缓存的重复引用被反序列化 - 每次都获取一个新的代理对象)。

希望这更有意义!

于 2016-07-26T10:08:47.433 回答
1

这只是制作必要对象的技巧。创建完全任意的用户定义类型的新实例更难。您传递给它的参数是什么__init__?如果它__init__有不良副作用怎么办?也许你可以改用它的__new__方法——但这需要什么参数?或者它甚至没有__new__方法,或者可能__new__有副作用......等等。

与弄清楚所有这些甚至可能是不可能的相比,这个技巧非常简单明了。

于 2014-05-02T11:49:08.013 回答