带锁的单例属性如何确保线程安全?

时间:2015-05-15 12:19:15

标签: c# multithreading design-patterns locking singleton

我很少使用单身人士,在这种情况下,这是合适的。在尝试调查其最佳实现时,我遇到了一些代码,这让我相信我不正确地理解括号如何封装“范围”。

public sealed class Singleton
{
    private static Singleton instance = null;
    private static readonly object padlock = new object();

    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            lock (padlock)
            {
                if (instance == null)
                {
                    instance = new Singleton();
                }
                return instance;
            }
        }
    }
} 

我很困惑当我尝试访问“实例”时会发生什么。假设我正在处理日志单例(我对单例的有用应用程序),它有一个方法“WriteLine(string line)”

我打电话的时候:

Singleton.Instance.WriteLine("Hello!");

它在执行“WriteLine?”

的整个方法时保持锁定

如果我将实例分配给外部变量,例如:

,该怎么办?
Singleton Console = Singleton.Instance;

现在对单身人士之外的单身人士有一个恒定的引用。 Console.WriteLine("Hello!")也像Singleton.Instance.WriteLine("Hello!")一样完全线程安全吗?

无论如何,我只是混淆了如何使单例线程安全,以及在显式访问属性时它是否只是线程安全。我以为Singlton.Instance.WriteLine("...")会首先提取实例,从而保留锁的范围,然后在返回的实例上执行WriteLine,因此在锁被释放后执行写操作。

任何有助于消除我对如何理解这些功能的误解的帮助。

3 个答案:

答案 0 :(得分:6)

  

在执行Singleton.Instance.WriteLine("Hello!");的整个方法时,WriteLine是否会保持锁定?

不,锁只保护你的单身人士的创造。 WriteLine执行解锁(当然,除非它在内部获得自己的锁)。

  

Console.WriteLine("Hello!")是否也完全是线程安全的,如Singleton.Instance.WriteLine("Hello!")

Singleton.Instance一样安全或不安全,因为锁定不会在Instance的吸气之外维持。

  

无论如何,我只是混淆了如何使单例线程安全

Lock使获取单例线程安全实例的过程成为可能。使单例线程安全的方法是一个不依赖于您的对象是否是单例的过程。没有简单的交钥匙一刀切的解决方案,使线程不安全的对象以线程安全的方式运行。你可以一次解决一个方法。

答案 1 :(得分:4)

不,锁定在return上结束,您对Instance执行的任何操作都是"外部"锁。

lock在这一点上的优势只有一个:

  • 它保证只能创建一个Singleton实例。

请注意,通常情况下,最好使用Lazy<>类。要获得相同的结果,您必须使用它:

public static Lazy<Singleton> Instance = new Lazy<Singleton>();

Lazy<T>可以使用三个modes,默认值ExecutionAndPublication,相当于该代码)

答案 2 :(得分:3)

  

任何有助于消除我对如何理解这些功能的误解的帮助。

Head First Design Patterns中,有一个使用&#34;代码磁铁&#34;的线程安全单例的一个很好的例子。您可以考虑两个线程可以执行相同代码的所有可能方式。它完成了三个列,一个用于两个线程中的每一个,第三列用于返回的假定单例的值。这是一个练习,您可以垂直放置代码片段以显示两个线程之间的操作顺序。我将尝试使用SO中的有限格式以及您的代码示例来重现它。

代码片段(没有锁定)将是:

get{
    if (instance == null){
        instance = 
            new Singleton(); }
    return instance; }

由于线程的执行方式,你可以找到一个可能导致返回类的两个实例的执行:

Thread One                   Thread Two                       Value instance
get{                                                          null
                             get{                             null
    if (instance == null){                                    null
                                 if (instance == null){       null
        instance = 
            new Singleton(); }                                Object_1
    return instance; }                                        Object_1
                                     instance = 
                                           new Singleton(); } Object_2
                                 return instance; }           Object_2

lock之后的get {Thread Two将无法继续(如上所述),直到Thread One执行return instance;并释放锁:

Thread One                   Thread Two                       Value instance
get{ [takes lock]                                             null
                             get{ [blocks on lock]            null
    if (instance == null){                                    null
        instance = 
            new Singleton(); }                                Object_1
    return instance; } [releases lock]                        Object_1
                                 [continues] 
                                 if (instance == null) {      Object_1
                                 return instance; }           Object_1