【问题标题】:Factory Initializer + Singleton工厂初始化器 + 单例
【发布时间】:2010-11-13 18:36:45
【问题描述】:

我为我的抽象工厂设计编写了一个非静态通用类实例化器,并使用单例方法确保每个客户端请求只初始化一个实例化器实例。

public sealed class FactoryInstantiator<T> where T: class
{
    private static readonly FactoryInstantiator<T> _instance = new Instantiator<T>();
    public static FactoryInstantiator<T> Instance
    {
        get
        {
            _client = HttpContext.Current.Session["ClientCode"].ToString();
            return _instance;
        }
    }

    private static string _client;
    private string _className;
    private string _fullyQualifiedClassName;
    private string _assemblyName;

    private FactoryInstantiator() { }

    public T CreateInstance()
    {
        string fullClassName = typeof(T).ToString();
        string[] splitClassName = fullClassName.Split('.');
        _className = splitClassName[2];
        _assemblyName = splitClassName[0] + "." + _client + "." + splitClassName[1];
        _fullyQualifiedClassName = _assemblyName + "." + _className;

        return (T)Activator.CreateInstance(Type.GetType(_fullyQualifiedClassName + "," + _assemblyName));
    }
}

我为每个客户端抽象了整个命名空间

namespace InventorySuite.Factory.BusinessLogic
{
    // abstract factory
    public abstract class InvoiceFactory
    {
        public abstract void Set() { }
    }
}

namespace InventorySuite.Client1.BusinessLogic
{
    // concrete invoice class for Client1
    public class Invoice : InvoiceFactory
    {
        public override void Set() { }
    }
}

namespace InventorySuite.Client2.BusinessLogic
{
    // concrete invoice class for Client2
    public class Invoice : InvoiceFactory
    {
        public override void Set() { }
    }
}


protected void Page_Load(object sender, EventArgs e)
{       
    InvoiceFactory clientInvoice;

    Session.Add("ClientCode", "Client1");
    clientInvoice = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
    clientInvoice.Set();

    Session["ClientCode"] = "Client2";
    clientInvoice = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
    clientInvoice.Set();

}

它运行良好并且已经过测试,但我的问题是关于它的效率/性能影响,因为我在这里使用反射,对于单例方法,如果它有多线程问题(afaik,单例实例将在所有客户)。我也将欣赏任何其他方法。谢谢

【问题讨论】:

  • 对我来说似乎是寻找问题的解决方案。
  • 在创建工厂的线路上加个锁,通过循环调用工厂数千次来测试性能,测量和比较时间。

标签: c# singleton factory


【解决方案1】:

您不会遇到任何多线程问题,因为您每次都在创建一个新实例。

关于性能。您可以测量创建 100 个实例的时间:

long ini = Environment.TickCount;
for (int i = 0; i < 100; i++)
{
    Session["ClientCode"] = "Client2";
    clientInvoice = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
    clientInvoice.Set();
}
long timeCreate100Instances = Environment.TickCount - ini;

使用reflection,性能损失在于加载程序集。我认为在您的情况下,您正在加载的类在同一个 dll 中,您也不会尝试任何性能问题。

在其他情况下,您可以在 CreateInstance() 方法中将 Assembly 对象缓存在 Hastable/Dictionary 中。

【讨论】:

    【解决方案2】:

    根据 Richard 和 Daniel 的建议,我能够减少使用缓存的反射对性能的影响。因此,我得出结论,Reflection 确实存在巨大的性能问题。

      public T CreateInstance()
        {
            string fullClassName = typeof(T).ToString();
            string[] splitClassName = fullClassName.Split('.');
            _className = splitClassName[2];
            _assemblyName = splitClassName[0] + "." + _client + "." + splitClassName[1];
            _fullyQualifiedClassName = _assemblyName + "." + _className;
    
            // use caching
            T obj;
            if (HttpContext.Current.Cache[_fullyQualifiedClassName] == null)
            {
                obj = (T)Activator.CreateInstance(Type.GetType(_fullyQualifiedClassName + "," + _assemblyName));
                HttpContext.Current.Cache.Insert(_fullyQualifiedClassName, obj, null, DateTime.Now.AddMinutes(1), TimeSpan.Zero);
            }
            else
            {
                obj = (T)HttpContext.Current.Cache[_fullyQualifiedClassName];
            }
    
            return obj;
        }
    
    
    protected void Page_Load(object sender, EventArgs e)
    {
        InvoiceFactory inv;
    
        Stopwatch globalTimer = Stopwatch.StartNew();
    
        //normal instantiation
        globalTimer = Stopwatch.StartNew();
        for (int x = 0; x <= 10000; x++)
            inv = new InventorySuit.Client1.BusinessLogic.Invoice;
        globalTimer.Stop();
        Response.Write(globalTimer.ElapsedMilliseconds + "<BR>");
        //result 0ms
    
    
        // using singleton factory w/o caching
        globalTimer = Stopwatch.StartNew();
        for (int x = 0; x <= 10000; x++)
            inv = new FactoryInstantiator<InvoiceFactory>().CreateInstance();
        globalTimer.Stop();
        Response.Write(globalTimer.ElapsedMilliseconds + "<BR>");
        //result 129ms
    
        // using singleton factory w/ caching
        for (int x = 0; x <= 10000; x++)
            inv = FactoryInstantiator<InvoiceFactory>.Instance.CreateInstance();
        globalTimer.Stop();
        Response.Write(globalTimer.ElapsedMilliseconds + "<BR>");
        //result 21ms
    
    
    
    }
    

    【讨论】:

    • 我在 for 循环 ctr 上添加了 1 个数字,结果很明显。
    • 小心,因为现在您使用的是共享实例,现在您可能会遇到多线程问题。您的实例现在必须是线程安全的。
    • 您由此得出结论认为反射存在巨大的性能问题?我的结论是,虽然普通反射比你的 chached 版本慢约 6 倍,但如果你能在 129 毫秒内实例化 10000 个项目,那对任何人来说都足够快。
    • @Daniel 你的意思是缓存实例?我将它存储在 Session State 上怎么样?由于不同的用户有不同的会话,这是否解决了多线程问题?谢谢
    • @Matt,我将 10000 替换为 1000000,没有缓存的反射实例慢 2 秒,非缓存实例慢 12 秒。在我的项目中,我可能会在一个循环中使用实例化器,该循环显示来自数据库的数千甚至数百万个数据 =)
    【解决方案3】:

    在会话状态中加载程序集以解决多线程问题。

       public T CreateInstance()
        {
            string fullClassName = typeof(T).ToString();
            string[] splitClassName = fullClassName.Split('.');
            _className = splitClassName[2];
            _assemblyName = splitClassName[0] + "." + _client + "." + splitClassName[1];
            _fullyQualifiedClassName = _assemblyName + "." + _className;
    
    
            T obj;
            var assemblies = HttpContext.Current.Session["ASSEMBLIES"] as Dictionary<string, T>;
    
            if (assemblies == null)
            {
                assemblies = new Dictionary<string, T>();
                assemblies.Add(_fullyQualifiedClassName, null);
                HttpContext.Current.Session.Add("ASSEMBLIES", assemblies);
            }
    
            obj = assemblies[_fullyQualifiedClassName] as T;
    
            if (obj == null)
            {
                obj = (T)Activator.CreateInstance(Type.GetType(_fullyQualifiedClassName + "," + _assemblyName));
                assemblies[_fullyQualifiedClassName] = obj;
            }
    
    
    
            return obj;
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多