在背景线程上初始化单例的常见模式

时间:2012-03-28 04:09:08

标签: c# .net multithreading design-patterns singleton

我目前有一个单例,初始化最多可能需要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()。

然而,由于两个原因,它感觉有点笨重: a)我将在启动时两次进入Init方法。 b)我想在单例内部进行线程化,但必须启动初始化。

是否有更清洁/更好/更好的方法来解决这个问题?

提前感谢大家的帮助。

**编辑:正如评论中指出的那样,Init被误认为是私有的。它应该是公开的并且已得到纠正。

3 个答案:

答案 0 :(得分:9)

使用静态构造函数触发它,并使用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();
    }
}

一旦触发,事件将保持信号状态,这意味着当Init方法完成时,Instance属性将尽快返回。

答案 1 :(得分:0)

您应该定义并调用单例类,如下所示......

  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;
                }
            }
        }

答案 2 :(得分:0)

&#39; d可以使用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() { }
    }
}