为什么在扭曲的pb系统中创建虚拟对象?

时间:2014-05-02 05:34:20

标签: python twisted perspective-broker

如下所示,在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__。对象,我根本没有看到制作假人的意义。为什么要创建假人?

2 个答案:

答案 0 :(得分:2)

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

首先,让我们考虑Copyable的简单情况。每次序列化时,您的同伴都会获得一个新的RemoteCopy。简单 - 无需跟踪。您的Copyable可以随时轻松收集垃圾。

接下来,Referenceable。每次序列化时,您的同伴都会获得一个新的RemoteReference。现在我们遇到了一个问题:如果您的同行仍然拥有RemoteReference,那么他们应该可以在Referenceable上调用方法,这意味着您的经纪人现在拥有对您Referenceable的强大引用。稍微复杂一点,但仍然相当简单:每次RemoteReference垃圾收集,RemoteReference.__del__,我们会发送一条decref消息,告诉发件人他们的Referenceable不再引用。当计数变为零时,可以消除强引用,并且它将被自然地垃圾收集。这是有效的,因为RemoteReference实际上是不可变的 - 它包含的是所讨论对象的不透明标识符,对代理的引用,以及其他内容。

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

这是一个循环引用,而且是一个糟糕的引用。我们需要一些方法来打破循环,以便我们交给应用程序的东西,它收集垃圾,每次服务器发送给我们RemoteCache的不同副本时跟踪。事实证明,我们可以这样做,并打破循环引用(请记住,这段代码早于python的GC!)通过haivng一个单独的实例对象,每个对象都有自己的{{1} }方法,用于发送的每个__del__。但是我们可以通过提供共享其内部状态(RemoteCache)的应用程序对象来保留一致性的幻觉,而不让让__dict__的对象通过让它们参与而无法收集一个循环。这样,当__del__个对象被垃圾收集时,它们每个都会发送一条消息,然后我们就会离开"真实的"一个(在您的示例中为RemoteCache)具有来自经纪人的强引用,它变为"零"引用,这也是它第一次复制的原因(你可以看到self只有一个其他的调用站点,这是对同一个可缓存的重复引用被反序列化的结果 - 得到一个每次新的代理对象。)

希望这更有意义!

答案 1 :(得分:1)

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

与弄清楚所有这些甚至可能无法实现相比,这个技巧非常简单直接。