Общий шаблон для инициализации синглтона в фоновом потоке - PullRequest
6 голосов
/ 28 марта 2012

В настоящее время у меня есть синглтон, для инициализации которого может потребоваться до 10 секунд. Однако я не хочу, чтобы мои пользователи штрафовали (ожидали) эту инициализацию, поэтому я предпочел бы загрузить этот компонент в фоновом потоке во время запуска приложения. Вот что у меня есть:

Singleton:

public class MySingleton
{
    private static MySingleton _instance;
    private static readonly object _locker = new object();

    private MySingleton() 
    {
        Init();
    }
    public static MySingleton Instance 
    { 
        if(_instance == null) _instance = new MySingleton();
        return _instance;
    }
    public void Init()
    {
        lock(_locker)
        {
            if(_instance != null) return;

            // long running code here...
        }
    }
}

Запуск приложения:

Task.Factory.StartNew(() => MySingleton.Instance.Init());

Этот код работает, защищает от двойной инициализации, защищает от крайнего случая пользователя, нуждающегося в нем, прежде чем он завершит инициализацию, а также защищает от того, кто забыл вызвать Init ().

Однако он выглядит немного неуклюжим по двум причинам: а) Я собираюсь войти в метод Init дважды при запуске. б) Я хотел бы выполнить потоки внутри синглтона, но что-то должно инициировать инициализацию.

Есть ли более чистый / приятный / лучший способ справиться с этим?

Заранее спасибо за помощь всем.

** РЕДАКТИРОВАТЬ: Как указано в комментариях, Иниции было ошибочно определено как частное. Это должно быть публично и было исправлено.

Ответы [ 3 ]

9 голосов
/ 28 марта 2012

Используйте статический конструктор для его запуска и ManualResetEvent для синхронизации.Это дает вам решение, где все делается в рамках реального класса.Поэтому он не зависит от того, кто-то должен вызывать ваш метод init.

public class MySingleton
{
    private static MySingleton _instance;
    private static ManualResetEvent _initEvent = new ManualResetEvent(false);

    static MySingleton() 
    {
        ThreadPool.QueueUserWorkItem(state => Init());
    }

    public static MySingleton Instance 
    {
        _initEvent.Wait();
        return _instance;
    }
    private static void Init()
    {
        _instance = new MySingleton();
        // long running code here...


        _initEvent.Set();
    }
}

Событие останется сигнальным после запуска, что означает, что свойство Instance вернется как можно скорее, когда метод Init будет завершен.

0 голосов
/ 28 марта 2012

Я бы пошел с Task<T>:

class Program
{
    static void Main(string[] args)
    {
        MySingleton.Init();

        Thread.Sleep(7000);

        Console.WriteLine("Getting instance...");
        var mySingleton = MySingleton.Instance;
        Console.WriteLine("Got instance.");
    }

    public class MySingleton
    {
        private static Lazy<MySingleton> instance;

        public static MySingleton Instance
        {
            get { return instance.Value; }
        }

        public static void Init()
        {
            var initTask = Task.Factory.StartNew(() =>
            {
                for(int i = 0; i < 10; i++)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine("Doint init stuff {0}...", i);
                }

                return new MySingleton();
            });

            instance = new Lazy<MySingleton>(() => initTask.Result);
        }

        private MySingleton() { }
    }
}
0 голосов
/ 28 марта 2012

Вы должны определить и вызвать синглтон-класс, как показано ниже ...

  var instance = MySingleton.Instance;
  while (true)
  {
     /// check for whether singleton initialization complete or not
     if (MySingleton.Initialized)
     {
       break;
     }
  }



  public class MySingleton
        {
            private static MySingleton _instance;
            private static readonly object _locker = new object();
            public static bool Initialized { get; set; }

            private MySingleton()
            {
                ThreadPool.QueueUserWorkItem(call => Init());
            }

            public static MySingleton Instance
            {
                get
                {
                    if (_instance == null)
                        _instance = new MySingleton();

                    return _instance;
                }
            }

            private void Init()
            {
                lock (_locker)
                {
                    if (Initialized)
                        return;
                    // long running code here...         
                    for (int i = 0; i < 10000; i++)
                    {

                    }
                    Initialized = true;
                }
            }
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...