如何使用C#实现AppDomain之间的高效通讯?

咨询区

  • open-collar

我有一个程序,需要在多个 AppDomains 之间传递大量的数据,我知道可以使用 Remoting 或者 命名管道 的方式,但个人感觉用在我这个场景下还是太重了,请问是否有轻量级的实现方案?.

回答区

  • V. S.

要想实现 AppDomains 之间的数据传送,实现的方式比较多,总结下来有如下几种:

1. CallContext

用 CallContext 相对轻量级,参考如下代码:

       public static void Main()
       {
           CallContext.LogicalSetData("Key", "My value");
           Console.WriteLine("{0} from {1}", CallContext.LogicalGetData("Key"),
           AppDomain.CurrentDomain.FriendlyName);

           var appDomain = AppDomain.CreateDomain("Worker");
           appDomain.DoCallBack(() => Console.WriteLine("{0} from {1}",
               CallContext.LogicalGetData("Key"),
               AppDomain.CurrentDomain.FriendlyName));
           AppDomain.Unload(appDomain);

           CallContext.FreeNamedDataSlot("Key");

           Console.ReadLine();
       }

2. AppDomain.SetData

如果你的业务特点只是实现 Host Domain 与 Child Domain 之间的通讯,而不是 Child Domain 到 Child Domain 之间的通讯,那就完全可以使用 AppDomain.SetData 了,参考如下代码:

       static void RunInChildDomain()
       {
           AppDomain childDomain = AppDomain.CreateDomain("friendlyName");
           string parameterValue = "notmii";
           childDomain.SetData("parameter", parameterValue);
           childDomain.DoCallBack(PrintName);
       }

       static void PrintName()
       {
           string Name = Convert.ToString(AppDomain.CurrentDomain.GetData("parameter"));
           Console.WriteLine(Name);
       }

3. 使用 MarshalByRefObject

实现方式是这样的,首先你需要创建一个继承于 MarshalByRefObject 的子类,然后在子类中就能获取到 ObjectHandle,最后通过 ObjectHandle 实现 AppDomain 之间的通讯,参考代码如下:

class Program
{
    public class ParentFacade : MarshalByRefObject
    {
        public List<string> Collection { get; set; }
        public void AddElement(string el)
        {
            Collection.Add(el);
        }
    }

    public class ChildFacade : MarshalByRefObject
    {
        public void AlterParentState(ObjectHandle handle)
        {
            var parent = handle.Unwrap() as ParentFacade;
            parent.AddElement("Message from child");
        }
    }

    static void Main(string[] args)
    {
        var child = AppDomain.CreateDomain("child");
        var childFacadeType = typeof(ChildFacade);
        var childProxy = child.CreateInstanceAndUnwrap(childFacadeType.Assembly.FullName, childFacadeType.FullName) as ChildFacade;

        var parentColl = new List<string>();
        var facade = new ParentFacade();
        facade.Collection = parentColl;
        var facadeHandle = new ObjectHandle(facade);
        childProxy.AlterParentState(facadeHandle);

        foreach (var item in parentColl)
            Console.WriteLine(item);
    }
}

点评区

有太多方式可以实现 跨进程, 跨机器 通讯,但放在 AppDomain 级别那就太重了,V. S. 大佬提供的三种方式很不错,学习了。