【发布时间】:2014-01-12 19:57:49
【问题描述】:
在以下编组情况下,我遇到了一个奇怪的问题。我有一个像这样的对象:
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 将其存储。然后,Primary 域检索此句柄,将其解包,并在将其注册到 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 都有一个构造和破坏,它发生在上面的标记行。问题是破坏似乎几乎立即发生。租约显然会立即收集垃圾(我可以看到 TrackingLease 析构函数在 GCFinalizer 线程上触发,并且时间不确定)。
我认为应该发生的是,在标记的行上,我得到了一个按值封送的新 MyData 对象。它应该包含对 ModuleDomain 中的 CallbackWrapper 的引用。当我构造新的 MyData 对象时,它们应该复制此引用。如果我随后将赞助商附加到 MyDataCopy.Callback 引用,它应该防止它被 GC'd,这应该让我在未来调用它。这不是正在发生的事情,但我不确定我做了什么来搞砸它。任何见解都会非常有帮助。
我可以提供任何其他信息来帮助回答这个问题吗?
【问题讨论】:
-
我认为您的 MyData 包含对代理 CallbackWrapper 的引用,而不是编组时的实际 CallbackWrapper。您可以检查是否使用
System.Runtime.Remoting.RemotingService.IsTransparentProxy(originals[0].Callback)。 -
这返回了“真”。这是一个 MarshalByRefObject - 它不应该作为参考回来吗?我怎么能影响这个?目标是让回调在 ModuleDomain 中执行。
标签: c# marshalling remoting appdomain copy-constructor