2014-01-12 34 views
0

我在下面的编组情况中遇到了一个奇怪的问题。我有这样一个对象:Marshalling包含MarshalByRefObj成员的Serializable数组

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包装base.InitializeLifetimeService这个新TrackingLease。我所看到的是:每个CallbackWrapper都有一个构造和破坏,它发生在上面的标记行。问题在于破坏似乎几乎立即发生;租约显然是从垃圾收集器中立即收集垃圾(我可以看到TrackingLease析构函数在GCFinalizer线程上触发,并且时间不确定)。

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

是否有任何其他信息可以帮助我回答这个问题?

+0

我觉得你迈德特包含代理CallbackWrapper,不整理在实际CallbackWrapper的参考。你可以使用'System.Runtime.Remoting.RemotingService.IsTransparentProxy(originals [0] .Callback)'检查。 – th1rdey3

+0

这返回'真'。这是一个MarshalByRefObject - 它不应该作为参考回来吗?我如何影响这个?目标是让回调在ModuleDomain中执行。 – Octavianus

回答

0

尝试添加下列到CallbackWrapper

public override object InitializeLifetimeService() 
{ 
    return null; 
} 
+0

这些回调可以在运行时更新,因此不适合退出内存管理。这样做的确会导致这种行为没有表现出来,但我的理解是它实际上只是泄露了CallbackWrapper,这实际上并不是一种选择,因为在应用程序的整个生命周期中可以有任意数量的它们。 – Octavianus