AppDomain 等待异步任务防止 SerializationException
Posted
技术标签:
【中文标题】AppDomain 等待异步任务防止 SerializationException【英文标题】:AppDomain await async Task prevent SerializationException 【发布时间】:2015-07-22 06:42:58 【问题描述】:我有一个 Windows 服务,它在运行时将程序集加载到另一个 AppDomain 中。然后它执行它们并最终卸载 AppDomain。问题是插件中的执行方法是异步任务,我得到了 SerializationException,因为 Task 没有从 MarshalByRefObject 继承。
我将插件封装在一个继承自 MarshalByRefObject 的代理中,但我不知道如何摆脱 SerializationException?
public interface IPlugin : IDisposable
Guid GUID get;
string Name get;
string Description get;
Task Execute(PluginPanel panel, string user);
代理:
[Serializable()]
public class PluginProxy : MarshalByRefObject, IPlugin
private IPlugin m_Plugin;
public bool Init(string file)
Assembly ass = Assembly.Load(AssemblyName.GetAssemblyName(file));
if (ass == null || ass.GetTypes() == null || ass.GetTypes().Length == 0)
return false;
foreach (Type type in ass.GetTypes())
if (type.IsInterface || type.IsAbstract)
continue;
if (type.GetInterface(typeof(IPlugin).FullName) != null)
m_Plugin = (IPlugin)Activator.CreateInstance(type);
return true;
return false;
public Guid GUID get return m_Plugin.GUID;
public string Name get return m_Plugin.Name;
public string Description get return m_Plugin.Description;
// I debugged and found out the error happens AFTER m_Plugin.Execute
// so the method runs well, but the return back to the pProxy.Execute is throwing the SerializationException
public async Task Execute(PluginPanel panel, string user) await m_Plugin.Execute(panel, user);
以及加载程序集并获取 SerializationException 的方法:
AppDomainSetup setup = new AppDomainSetup();
// some setup stuff
AppDomain dom = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null, setup);
PluginProxy pProxy = (PluginProxy)dom.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().CodeBase, typeof(PluginProxy).FullName);
pProxy.Init(app.Apppath);
// I await the task later in code, because the user can cancel the execution
try tExe = pProxy.Execute(panel, user.Username);
catch (System.Runtime.Serialization.SerializationException e)
// runs always in this catch, even if no Exception from the plugin was thrown
catch (Exception e) AddToErrorLog(panel.PanelName, e);
finally
pProxy.Dispose();
AppDomain.Unload(dom);
也许我加载插件的整个概念是错误的?
【问题讨论】:
您的Execute
也返回Task
。在你的包装器中同步等待Execute
。
so 而不是 await m_Plugin.Execute(panel, user);我应该这样做:m_Plugin.Execute(panel, user);?或者这个:m_Plugin.Execute(panel, user).Wait();?但是 Wait() 会阻塞整个线程?
好的,看看 Stephen Toub post。你应该为你的情况做些小改动。
谢谢,我查看了帖子,但没有异步?他创建了一个返回 TaskDoWorkInOtherDomain
返回的Task。
【参考方案1】:
感谢 Hamlet Hakobyan 和 Stephen Toub 的帖子,我想我能够解决问题。
我替换了调用者的线路
try tExe = pProxy.Execute(panel, user.Username);
与
tExe = DoWorkInOtherDomain(pProxy, panel, user.Username);
以及DoWorkInOtherDomain方法:
private Task DoWorkInOtherDomain(PluginProxy pProxy, PluginPanel panel, string user)
var ch = new MarshaledResultSetter<string>();
pProxy.Execute(panel, user, ch);
return ch.Task;
最后是代理类:
Task.Run(() =>
try
m_Plugin.Execute(panel, user).Wait();
catch (AggregateException e)
if (e.InnerExceptions != null)
foreach (Exception ein in e.InnerExceptions)
AddToErrorLog(panel.PanelName, ein);
catch (Exception e) AddToErrorLog(panel.PanelName, e);
finally ch.SetResult(AppDomain.CurrentDomain.FriendlyName);
);
我需要在
中调用Wait()m_Plugin.Execute(panel, user).Wait();
它从插件中捕获异常,因此一切正常。 Wait() 调用应该只阻止 Task.Run 而不是其他任务。
谁能告诉我这是一个好的解决方案还是我应该改变什么?我不需要结果,所以我只需要:
ch.SetResult(AppDomain.CurrentDomain.FriendlyName);
因为没有结果我不知道该怎么做。
【讨论】:
我现在唯一的问题是我无法从插件中捕获异常。即使我扔一个,m_Plugin.Execute(panel, user);
也不会抓住它。如果Execute
方法抛出异常并且'PluginProxy' 中的m_Plugin.Execute(panel, user);
和await m_Plugin.Execute(panel, user);
都没有捕获Exception
,它会去哪里?如果抛出未处理的异常,应用程序不应该崩溃吗?以上是关于AppDomain 等待异步任务防止 SerializationException的主要内容,如果未能解决你的问题,请参考以下文章