在.net中通过ApplicationDomain边界实现回调
我使用应用程序域动态加载 dll,以便在需要时卸载。如果加载的 dll 中的任务自行终止,我无法工作的是创建的 Appdomain 中的回调方法。
到目前为止,我所拥有的
public interface IBootStrapper
{
void AsyncStart();
void StopAndWaitForCompletion();
event EventHandler TerminatedItself;
}
以及“Starter”端
private static Procedure CreateDomainAndStartExecuting()
{
AppDomain domain = AppDomain.CreateDomain("foo", null, CODEPATH, string.Empty, true);
IBootStrapper strapper = (IBootStrapper)domain.CreateInstanceAndUnwrap(DYNAMIC_ASSEMBLY_NAME, CLASSNAME);
strapper.ClosedItself += OnClosedItself;
strapper.AsyncStart();
return delegate
{
strapper.StopAndWaitForCompletion();
AppDomain.Unload(domain);
};
}
会导致程序集未找到异常,因为 OnClosedItself() 是仅 Starter 已知的类型的方法,该方法不存在于应用程序域中。
如果我将 OnClosedItself 作为委托包装在可序列化的类中,它是相同的。
有什么建议吗?
编辑: 我想做的是建立一个自我更新任务。因此,我创建了一个启动器,如果有新版本可用,它可以停止并重新创建任务。但如果任务从其他地方停止,它也应该通知启动器终止。
// 从问题中删除了很多临时代码
编辑2: Haplo 为我指明了正确的方向。我能够使用信号量实现回调。
I load a dll dynamically using an Applicationdomain to unload when nessesary. What i cant get to work is a callback-method from the created Appdomain if the task in the loaded dll terminates itself.
What i have so far
public interface IBootStrapper
{
void AsyncStart();
void StopAndWaitForCompletion();
event EventHandler TerminatedItself;
}
and the "Starter" side
private static Procedure CreateDomainAndStartExecuting()
{
AppDomain domain = AppDomain.CreateDomain("foo", null, CODEPATH, string.Empty, true);
IBootStrapper strapper = (IBootStrapper)domain.CreateInstanceAndUnwrap(DYNAMIC_ASSEMBLY_NAME, CLASSNAME);
strapper.ClosedItself += OnClosedItself;
strapper.AsyncStart();
return delegate
{
strapper.StopAndWaitForCompletion();
AppDomain.Unload(domain);
};
}
which results in a assembly not found exception because OnClosedItself() is a method of a type only known to the Starter, which is not present in the appdomain.
If I wrapp the OnClosedItself as delegate in a serializable class it's the same.
Any suggestions?
Edit:
What I'm trying to do is building a selfupdating task. Therefor i created a starter, which can stop and recreate the task if a new version is available. But if the task is stopped from somewhere else, it should also notify the starter to terminate.
// stripped a lot of temporary code from the question
EDIT 2:
Haplo pointed me to the right direction. I was able to implement the callback with semaphores.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我通过使用具有共享类型的第三个程序集解决了这种情况(在您的例子中是 IBoostrapper 的实现)。就我而言,我有更多的类型和逻辑,但对您来说,仅针对一种类型创建一个程序集可能有点过分了...
也许您更愿意使用共享命名互斥体?然后您可以同步 2 个 AppDomains 任务...
编辑:
您正在主 Appdomain 上创建互斥体,并且也作为初始拥有,因此它永远不会在 WaitOne() 上停止,因为您已经拥有它。
例如,您可以在 IBootstrapper 实现类内的生成的 Appdomain 上创建互斥体(与最初拥有的一样)。 CreateInstanceAndUnwrap 调用返回后,互斥锁应该存在并且由 Bootstrapper 拥有。因此,您现在可以打开互斥体(调用OpenExisting,以便确定您正在共享它),然后您可以对其进行 WaitOne。一旦生成的 AppDomain 引导程序完成,您就可以释放互斥体,主 Appdomain 将完成工作。
互斥体是系统范围的,因此它们可以跨进程和应用程序域使用。查看 MSDN Mutex 的备注部分a>
编辑:如果您无法使其与互斥体一起使用,请参阅下一个使用信号量的简短示例。这只是为了说明这个概念,我没有加载任何附加程序集等......默认 AppDomain 中的主线程将等待信号量从生成的域中释放。当然,如果您不希望主AppDomain终止,则不应允许主线程退出。
I solved this situation by using a third assembly that had the shared type (in your case the implementation for IBoostrapper). In my case I had more types and logic, but for you it might be a bit overkill to have an assembly just for one type...
Maybe you would prefer to use a shared named Mutex? Then you can synchronize the 2 AppDomains tasks...
EDIT:
You are creating the mutex on the main Appdomain, and also as initially owned, so it will never stop on WaitOne() beacuse you already own it.
You can, for example, create the Mutex on the spawned Appdomain inside the IBootstrapper implementing class, as initially owned. After the CreateInstanceAndUnwrap call returns, the mutex should exist and it's owned by the Bootstrapper. So you can now open the mutex (call OpenExisting so you are sure that you're sharing it), and then you can WaitOne on it. Once the spawned AppDomain bootstrapper completes, you can Release the mutex, and the main Appdomain will complete the work.
Mutexes are system wide, so they can be used across processes and AppDomains. Take a look on the remarks section of MSDN Mutex
EDIT: If you cannot make it work with mutexes, see the next short example using semaphores. This is just to illustrate the concept, I'm not loading any additional assembly, etc.... The main thread in the default AppDomain will wait for the semaphore to be released from the spawned domain. Of course, if you don't want the main AppDomain to terminate, you should not allow the main thread to exit.
我最近使用了另一种方法,它可能比信号量方法更简单,只需在两个应用程序域都可以引用的程序集中定义一个接口。然后创建一个实现该接口并从 MarshalByRefObject 派生的类。
接口可以是任何东西,请注意,当调用越过应用程序域边界时,接口中任何方法的任何参数都必须进行序列化
然后在父应用程序域可以的程序集中使用我定义了从 MarshalByRefObject 派生的该接口的实现:
因此,当我创建子应用程序域时,我只需向其传递一个 new FailureNotifier() 的实例。由于 MarshalByRefObject 是在父域中创建的,因此对其方法的任何调用都将自动编组到创建它的应用程序域,无论从哪个应用程序域调用它。由于调用将从另一个线程发生,因此无论接口方法做什么都需要是线程安全的
I used another approach recently that might be simpler than the semaphore approach, just define an interface in an assembly that both appdomains can reference. Then create a class that implements that interface and derivces from MarshalByRefObject
The interface would be whatever, note that any arguments to any methods in the interface will have to be serialized when the call goes over the appdomain boundary
Then in an assembly that the parent appdomain can use I define an implementation of that interface that derives from MarshalByRefObject:
So when I create the child appdomain I simply pass it an instance of new FailureNotifier(). Since the MarshalByRefObject was created in the parent domain then any calls to its methods will automatically get marshalled over to the appdomain it was created in regardless of what appdomain it was called from. Since the call will be happening from another thread whatever the interface method does will need to be threadsafe
感谢 Haplo 我能够实现同步如下
thanks to Haplo i was able to implement the synchronization as follows