C# 在后台线程上初始化单例的公共模式

C# 在后台线程上初始化单例的公共模式,c#,.net,multithreading,design-patterns,singleton,C#,.net,Multithreading,Design Patterns,Singleton,我目前有一个单例,它最多需要10秒来初始化。然而,我不希望我的用户因为这个初始化而受到惩罚(等待),所以我宁愿在应用程序启动期间在后台线程上引导这个组件。以下是我所拥有的: 单身人士: public class MySingleton { private static MySingleton _instance; private static readonly object _locker = new object(); private MySingleton()

我目前有一个单例,它最多需要10秒来初始化。然而,我不希望我的用户因为这个初始化而受到惩罚(等待),所以我宁愿在应用程序启动期间在后台线程上引导这个组件。以下是我所拥有的:

单身人士:

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被错误地定义为私有。它应该是公开的,并且已经被纠正了

您应该定义并调用singleton类,如下所示

  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;
                }
            }
        }
var instance=MySingleton.instance;
while(true)
{
///检查单例初始化是否完成
if(MySingleton.Initialized)
{
打破
}
}
公共课米辛格尔顿
{
私有静态MySingleton_实例;
私有静态只读对象_locker=new object();
公共静态bool已初始化{get;set;}
二等兵迈辛格尔顿()
{
QueueUserWorkItem(call=>Init());
}
公共静态MySingleton实例
{
得到
{
if(_instance==null)
_instance=new MySingleton();
返回_实例;
}
}
私有void Init()
{
锁(储物柜)
{
如果(已初始化)
返回;
//长时间运行的代码在这里。。。
对于(int i=0;i<10000;i++)
{
}
初始化=真;
}
}
}

使用静态构造函数触发它,并使用
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方法完成后尽快返回。

我将执行
任务。

类程序
{
静态void Main(字符串[]参数)
{
MySingleton.Init();
睡眠(7000);
WriteLine(“获取实例…”);
var mySingleton=mySingleton.Instance;
WriteLine(“获得实例”);
}
公共课米辛格尔顿
{
私有静态惰性实例;
公共静态MySingleton实例
{
获取{return instance.Value;}
}
公共静态void Init()
{
var initTask=Task.Factory.StartNew(()=>
{
对于(int i=0;i<10;i++)
{
睡眠(1000);
WriteLine(“Doint init stuff{0}…”,i);
}
返回新的MySingleton();
});
instance=newlazy(()=>initTask.Result);
}
私有MySingleton(){}
}
}

您应该使用
Lazy
来初始化您的单例,以便在实际使用单例之前卸载初始化。Jon在那篇文章中指出“我个人偏好解决方案4:”这不是懒惰的解决方案。请阅读这篇文章,了解他的推理和更多信息。@MitchWheat-Heh,我想(在我读了10多次之后)我从来没有读过这篇文章。这将教会我保持沉默(可能不是这样)当Init()是一个私有方法时,你如何在启动时调用它?@M.Babcock:最后一部分只是(相对而言)最近添加的。非常好的解决方案!谢谢但有两个问题:a)是否有理由将UserWorkItem而不是Task.Factory.StartNew()排入队列?b) 是否需要使用手动重置事件而不是自动重置事件?a)否,个人偏好。b) 对
AutoResetEvent
将在第一个
Wait
已完成时重置信号并阻止所有调用者轻微问题:仅在第一次使用类成员时调用静态构造函数。因此,在应用程序启动期间不会运行Init()。@pdalb01是正确的。提出的解决方案(最迟)将在首次使用时运行。您可以添加一个伪函数来触发命令的实例化。类似于publicstaticboolcreateInstanceInBackground(){return}\u instance==null;}的东西,如果您想强制启动,可以在主构造函数中调用它。
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() { }
    }
}