• CLR via C# 读书笔记 6-2 不同AppDomain之间的通信 z


    跨AppDomain通信有两种方式

      1.Marshal By reference : 传递引用

      2.Marshal By Value : 把需要传递的对象 通过序列化反序列化的方式传递过去(值拷贝)

        只有标记为 可序列化 Serializable 的类才能通过 Marshal By Value的方式通信

    以下代码描述了几种跨域通信的情况

    1.AppDomain是CLR的内部行为,windows完全不清楚有AppDomain的存在

    2.在新的域中加载Assembly和Type最好用完整限定名(如果直接加载Type, CLR会自动加载Type所在的和所用到的Assembly)

    3.默认情况下新建的应用程序域使用和当前域一样的权限设置,如果你需要手动指定权限,那么构造一个PermissionSet参数传给CreateDomain

    4.同样的,如果想给应用程序设置不同的配置,构造一个AppDomainSetup传给他(可以设置配置文件,程序路径,影像拷贝什么的..)

    5.跨域访问的时候不会发生线程的上下文切换

    6.CreateInstanceAndUnwrap 默认调用对象的无参构造函数,当然,有些重载允许你调用有参构造函数

    7.只有继承了System.MarshalByRefObject的对象才能以引用方式传递 (这是一个基类....c#又不允许多继承....还是挺麻烦的)

    8.只有标记了了Serializable 的对象才能以值方式传递(内部的序列化反序列化) ,这里有比较严重的性能损耗

    代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Reflection;
    using System.Runtime.Remoting;

    namespace TestConsole
    {
    publicclass Program
    {
    staticvoid Main(string[] args)
    {

    // 获取一个指向应用程序域的引用
    AppDomain adCallingThreadDomain = Thread.GetDomain();
    // 每一个应用程序域都会被分配一个名字帮助调试 ,以下代码获取引用程序域的名字
    String callingDomainName = adCallingThreadDomain.FriendlyName;
    // 获取应用程序域的完整名
    String exeAssembly = Assembly.GetEntryAssembly().FullName;
    AppDomain ad2
    =null;

    // ************************************************************************************************************
    // 使用Marshal-by-Reference的方式跨域通信
    Console.WriteLine("{0}Demo #1", Environment.NewLine);
    // 建立一个域,安全和配置均使用当前域的设置
    ad2 = AppDomain.CreateDomain("AD #2", null, null);
    MarshalByRefType mbrt
    =null;
    // 加载Assembly到新的域,new一个对象并且返回到当前域 (实际上返回的是一个引用代理)
    mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "TestConsole.MarshalByRefType");
    Console.WriteLine(
    "Type={0}", mbrt.GetType()); // CLR 对GetType做了一些手脚,返回的是被代理数据的真实类型
    // 以下代码证明我们获取的是一个代理对象
    Console.WriteLine("Is proxy={0}", RemotingServices.IsTransparentProxy(mbrt));
    // 我们调用了代理类的SomeMethod() , 代理类跨域访问真正的对象
    mbrt.SomeMethod();
    // 卸载新建的那个应用程序域
    AppDomain.Unload(ad2);
    // mbrt refers to a valid proxy object; the proxy object refers to an invalid AppDomain
    try
    {
    // 再次调用代理类的SomeMethod() , 由于域已经被卸载 抛出一个异常
    mbrt.SomeMethod();
    Console.WriteLine(
    "Successful call.");
    }
    catch (AppDomainUnloadedException)
    {
    Console.WriteLine(
    "Failed call.");
    }

    // ************************************************************************************************************
    // 使用Marshal-by-Value 的方式跨域通信
    Console.WriteLine("{0}Demo #2", Environment.NewLine);
    // 新建域
    ad2 = AppDomain.CreateDomain("AD #2", null, null);
    // 加载程序集并创建代理类
    mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "TestConsole.MarshalByRefType");
    // 该方法返回了一个值的拷贝 marshaled by value (not be reference).
    MarshalByValType mbvt = mbrt.MethodWithReturn();
    // 证明返回值不是一个代理类
    Console.WriteLine("Is proxy={0}", RemotingServices.IsTransparentProxy(mbvt));
    // 查看返回值是谁创建的
    Console.WriteLine("Returned object created "+ mbvt.ToString());
    // 卸载应用程序域
    AppDomain.Unload(ad2);
    // 由于是值传递,那么卸载域对函数没有影响 // marshaled by value
    try
    {
    //不会有异常抛出
    Console.WriteLine("Returned object created "+ mbvt.ToString());
    Console.WriteLine(
    "Successful call.");
    }
    catch (AppDomainUnloadedException)
    {
    Console.WriteLine(
    "Failed call.");
    }
    // ************************************************************************************************************
    // non-marshalable type 跨域通信
    Console.WriteLine("{0}Demo #3", Environment.NewLine);
    ad2
    = AppDomain.CreateDomain("AD #2", null, null);
    mbrt
    = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "TestConsole.MarshalByRefType");
    // 没有标记为 Serializable 的类型对象 不能通过marshaled by value 跨域通信
    NonMarshalableType nmt = mbrt.MethodArgAndReturn(callingDomainName);

    }
    }

    //即使没有标记为Serializable 也可以通过 marshaled-by-reference 的方式跨域通信
    publicsealedclass MarshalByRefType : MarshalByRefObject
    {
    public MarshalByRefType()
    {
    Console.WriteLine(
    "{0} ctor running in {1}",
    this.GetType().ToString(), Thread.GetDomain().FriendlyName);
    }
    publicvoid SomeMethod()
    {
    Console.WriteLine(
    "Executing in "+ Thread.GetDomain().FriendlyName);
    }
    public MarshalByValType MethodWithReturn()
    {
    Console.WriteLine(
    "Executing in "+ Thread.GetDomain().FriendlyName);
    MarshalByValType t
    =new MarshalByValType();
    return t;
    }
    public NonMarshalableType MethodArgAndReturn(String callingDomainName)
    {
    // NOTE: callingDomainName is [Serializable]
    Console.WriteLine("Calling from ‘{0}’ to ‘{1}’.",
    callingDomainName, Thread.GetDomain().FriendlyName);
    NonMarshalableType t
    =new NonMarshalableType();
    return t;
    }
    }

    //只有标记为 Serializable 的类型 才能用marshaled by value 的方式跨域通信
    [Serializable]
    publicsealedclass MarshalByValType : Object
    {
    private DateTime m_creationTime = DateTime.Now; // NOTE: DateTime is [Serializable]
    public MarshalByValType()
    {
    Console.WriteLine(
    "{0} ctor running in {1}, Created on {2:D}",
    this.GetType().ToString(),
    Thread.GetDomain().FriendlyName,
    m_creationTime);
    }
    publicoverride String ToString()
    {
    return m_creationTime.ToLongDateString();
    }
    }

    // 没有标记为 Serializable 的类型 不能用marshaled by value 的方式跨域通信
    // [Serializable]
    publicsealedclass NonMarshalableType : Object
    {
    public NonMarshalableType()
    {
    Console.WriteLine(
    "Executing in "+ Thread.GetDomain().FriendlyName);
    }
    }
    }
  • 相关阅读:
    简易httpserver 和客户端调用
    exe打包简易教程
    图像二值化和显示3D 的算法实现
    gitlab 生成标签
    Nginx 使用小结
    FFMPEG 切片过于占用cpu的问题
    神奇 linux 命令行 窗体穿越
    使用 SSH 在 Linux 上远程调试 .NET Core 实例 演示
    CentOS 7 yum 安装 Nginx 以及 TCP流转发
    Centos7 自动挂载分区硬盘 u盘的操作
  • 原文地址:https://www.cnblogs.com/zeroone/p/3621933.html
Copyright © 2020-2023  润新知