包含MarshalByRefObj成员的Serializable编组数组

时间:2014-01-12 19:57:50

标签: c# marshalling remoting appdomain copy-constructor

在以下编组情况下,我遇到了一个奇怪的问题。我有一个像这样的对象:

class CallbackWrapper : MarshalByRefObj
{
    private Func<String, bool> _callback;
    public CallbackWrapper(Func<String, bool> callback)
    {
        _callback = callback;
    }

    public bool Execute(String input)
    {
        return _callback(input);
    }
}

[Serializable]
class MyData
{
    public CallbackWrapper Callback {get; private set;}
    public UnfriendlyType Data {get; private set;}
    public MyData(UnfriendlyType data, Func<String, bool> callback)
    {
        Data = data;
        Callback = new CallbackWrapper(callback);
    }

    public MyData(MyData other, UnfriendlyType data)
    {
        Data = data;
        Callback = other.Callback;
    }
}

class MyModule : MarshalByRefObject
{
    public MyData[] Data {get; private set;}
    public MyModule()
    {
        Data = //etc
    }
}

我有两个appdomains,我将其称为Primary和ModuleDomain。我通过调用ModuleDomain.DoCallback(MethodWhichInstantiatesTheMyModule)在ModuleDomain中创建MyModule,它创建MyModule对象并通过SetData为域存储它。然后,主域将检索此句柄,将其解包,并在将代理注册到ISponsor后存储代理。这部分有效。完成此操作后,我为MyModule触发一个事件,指示新数据可用(这在主AppDomain中发生):

HandleNewData(this, new NewDataAvailableEventArgs(myModuleProxy));

[Serializable]
class NewDataAvailableEventArgs : EventArgs
{
    public MyModule Module {get; private set;}
    public NewDataAvailableEventArgs(MyModule module)
    {
        Module = module;
    }
}

最终采用以下方法:

void ProcessNewData(object sender, NewDataAvailableEventArgs e)
{
    var localData= new List<MyData>();
    var originals = e.Module.Data; // ***** This is where leases get constructed and destructed *****
    // Manually mess with the UnfriendlyType member of each element in originals (this is unrelated, but is why we have to do this copy construction in the Primary AppDomain).
    localData.AddRange(from data in originals let originalData = originals[mappingFunc(data)] select new MyData(originalData, data));
    // Do more processing, add a sponsor to each of MyData.Callback, etc.
}

这就是问题所在。我将这些MyData对象存储在本地,并与MyModule建立关联。后来,我使用了回调。我们的想法是CallbackWrapper将确保回调在ModuleDomain中执行,而不是在Primary中执行。这适用于五分钟,但五分钟后,CallbackWrapper对象断开连接并抛出异常。这很奇怪,因为我明确地向每个MyData注册赞助商。当我覆盖CallbackWrapper的租用代码时,我可以看到发生了什么:

class TrackingLease : ILease
{
    private static uint LeaseIdCurrent = 0;
    private uint LeaseId;
    private ILease _baseLease;
    public TrackingLease(ILease lease)
    {
        _baseLease = lease;
        LeaseId = LeaseIdCurrent++;
        Console.WriteLine("TrackingLease {0} constructed.\n", LeaseId);
    }
    ~TrackingLease()
    {
        Console.WriteLine("TrackingLease {0} destructed.\n", LeaseId);
    }
    // etc
}

当然,我覆盖了CallbackWrapper的InitializeLifetimeService,用这个新的TrackingLease包装base.InitializeLifetimeService。我看到的是:每个CallbackWrapper都有一个构造和破坏,它出现在上面的标记行上。问题是破坏似乎几乎立即发生;租约显然是从垃圾收集垃圾(我可以看到在GCFinalizer线程上触发TrackingLease析构函数,时间是不确定的)。

我认为应该发生的是,在标记的行中,我得到一个新的MyData对象,它按值进行封送处理。它应该包含对ModuleDomain中的CallbackWrapper的引用。当我构造新的MyData对象时,他们应该复制这个引用。如果我然后将赞助商附加到MyDataCopy.Callback引用,它应该使它不被GC,这应该让我将来调用它。这不是正在发生的事情,但我不确定我做了什么来搞砸它。任何见解都会非常有用。

我能提供哪些有助于回答此问题的其他信息?

1 个答案:

答案 0 :(得分:0)

尝试将以下内容添加到CallbackWrapper

public override object InitializeLifetimeService()
{
  return null;
}