Заводской инициализатор + синглтон

Я написал нестатический экземпляр универсального класса для своего дизайна абстрактной фабрики и использую подход Singleton, чтобы гарантировать, что только 1 экземпляр экземпляра экземпляра будет инициализирован для каждого клиентского запроса.

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();

}

Он работает хорошо и уже протестирован, но мой вопрос касается его эффективности / производительности, поскольку я использую здесь отражение, и для подхода Singleton, если у него есть проблемы с многопоточностью (afaik, экземпляр singleton будет использоваться всеми клиентами) . Я также буду признателен за любой другой подход к этому вопросу. Благодарность


person CSharpNoob    schedule 13.11.2010    source источник
comment
Мне кажется, что это решение, ищущее проблему.   -  person Mark    schedule 13.11.2010
comment
Установите блокировку вокруг линии, которая создает фабрику, и протестируйте производительность, вызывая фабрику тысячи раз в цикле, измеряя и сравнивая время.   -  person Richard Astbury    schedule 13.11.2010


Ответы (3)


У вас не будет проблем с многопоточностью, поскольку вы каждый раз создаете новый экземпляр.

О спектакле. Вы можете измерить время создания 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, вы также не будете экспериментировать с какой-либо проблемой производительности.

В противном случае вы можете кэшировать Assembly объекты в Hastable/Dictionary вашего CreateInstance() метода.

person Daniel Peñalba    schedule 13.11.2010

Используя предложения Ричарда и Дэниела, я смог уменьшить влияние отражения на производительность с помощью кэширования. Поэтому я прихожу к выводу, что у 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



}
person CSharpNoob    schedule 13.11.2010
comment
Я добавил к нему 1 цифру для loop ctr, и результат был красноречивым. - person CSharpNoob; 13.11.2010
comment
Будьте осторожны, потому что теперь вы используете общий экземпляр, теперь у вас могут возникнуть проблемы с многопоточностью. Теперь ваши экземпляры должны быть потокобезопасными. - person Daniel Peñalba; 13.11.2010
comment
вы делаете вывод, что рефлексия имеет огромные проблемы с производительностью из-за этого? Я бы сделал вывод, что, хотя простое отражение в ~ 6 раз медленнее, чем ваша измененная версия, если вы можете создать экземпляр 10000 элементов за 129 мс, этого достаточно быстро для всех. - person Matt Ellen; 13.11.2010
comment
@ Даниэль, вы имеете в виду экземпляр Cache? как насчет того, чтобы вместо этого сохранить его в состоянии сеанса? Решает ли это проблему многопоточности, поскольку у разных пользователей разные сеансы? Благодарность - person CSharpNoob; 13.11.2010
comment
@Matt, я заменяю 10000 на 1000000, и отраженный экземпляр без кеша на 2 секунды медленнее, а некэшированный - на 12 секунд медленнее. В моем проекте я мог бы использовать инстанциатор внутри цикла, который отображает тысячи или даже миллионы данных из базы данных =) - person CSharpNoob; 13.11.2010

Загрузка сборок в состоянии сеанса для решения проблемы многопоточности.

   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;
    }
person CSharpNoob    schedule 13.11.2010