线程安全的C#单子模式[英] Thread Safe C# Singleton Pattern

本文是小编为大家收集整理的关于线程安全的C#单子模式的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我有一些有关此处记录的单身模式的问题: http://msdn.microsoft.com/en-en-us/en-us/library/library/fff650316.aspx

以下代码是文章中的摘录:

using System;

public sealed class Singleton
{
   private static volatile Singleton instance;
   private static object syncRoot = new object();

   private Singleton() {}

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

         return instance;
      }
   }
}

特别是在上面的示例中,是否需要将实例与锁之前和之后两次NULL进行比较?这是必要的吗?为什么不先执行锁并进行比较?

简化以下内容有问题吗?

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

         return instance;
      }
   }

执行锁是否昂贵?

推荐答案

与简单的指针检查instance != null.

您在此看到的图案称为双检查锁定.它的目的是避免仅需要一次只需要一次(首次访问单例时)的昂贵锁操作.实现之所以如此,是因为它还必须确保在初始化单例时不会引起线程竞赛条件引起的错误.

以这种方式考虑:裸露的null检查(无lock)可以保证只有在答案为"是的,对象已经构造"时,才能为您提供正确的可用答案.但是,如果答案是"尚未构造的",那么您没有足够的信息,因为您真正想知道的是"尚未构建,也没有其他线程打算尽快构建它" .因此,您将外部检查用作非常快速的初始测试,并启动适当的,无错误但"昂贵"的过程(锁定然后检查),仅当答案为"否".

在大多数情况下,上述实现足够好,但是在这一点上,最好去阅读乔恩·斯基特(Jon Skeet

其他推荐答案

Lazy<T>版本:

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy
        = new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance
        => lazy.Value;

    private Singleton() { }
}

需要.NET 4和C#6.0(VS2015)或更新.

其他推荐答案

执行锁:很便宜(比无效测试贵).

在另一个线程时执行锁定:您可以获得他们在锁定时仍要做的任何事情的成本,并添加到您自己的时间中.

当另一个线程有另一个线程时,执行锁定,其他数十个线程也在等待:残废.

出于绩效原因,您始终想拥有另一个线程想要的锁,尽可能短的时间.

当然,关于"宽"锁的推理要比狭窄的锁比狭窄更容易,因此值得从需要的情况下进行广泛和优化,但是在某些情况下,我们从经验和熟悉的情况下了解到较窄的模式.

(顺便说一句,如果您可能只使用private static volatile Singleton instance = new Singleton(),或者如果您可能不使用单例,而是使用静态类,则两者都在这些问题上更好).

本文地址:https://www.itbaoku.cn/post/627525.html

问题描述

I have some questions regarding the the singleton pattern as documented here: http://msdn.microsoft.com/en-us/library/ff650316.aspx

The following code is an extract from the article:

using System;

public sealed class Singleton
{
   private static volatile Singleton instance;
   private static object syncRoot = new object();

   private Singleton() {}

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

         return instance;
      }
   }
}

Specifically, in the above example, is there a need to compare instance to null twice, before and after the lock? Is this necessary? Why not perform the lock first and make the comparison?

Is there a problem in simplifying to the following?

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

         return instance;
      }
   }

Is the performing the lock expensive?

推荐答案

Performing the lock is terribly expensive when compared to the simple pointer check instance != null.

The pattern you see here is called double-checked locking. Its purpose is to avoid the expensive lock operation which is only going to be needed once (when the singleton is first accessed). The implementation is such because it also has to ensure that when the singleton is initialized there will be no bugs resulting from thread race conditions.

Think of it this way: a bare null check (without a lock) is guaranteed to give you a correct usable answer only when that answer is "yes, the object is already constructed". But if the answer is "not constructed yet" then you don't have enough information because what you really wanted to know is that it's "not constructed yet and no other thread is intending to construct it shortly". So you use the outer check as a very quick initial test and you initiate the proper, bug-free but "expensive" procedure (lock then check) only if the answer is "no".

The above implementation is good enough for most cases, but at this point it's a good idea to go and read Jon Skeet's article on singletons in C# which also evaluates other alternatives.

其他推荐答案

The Lazy<T> version:

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy
        = new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance
        => lazy.Value;

    private Singleton() { }
}

Requires .NET 4 and C# 6.0 (VS2015) or newer.

其他推荐答案

Performing a lock: Quite cheap (still more expensive than a null test).

Performing a lock when another thread has it: You get the cost of whatever they've still to do while locking, added to your own time.

Performing a lock when another thread has it, and dozens of other threads are also waiting on it: Crippling.

For performance reasons, you always want to have locks that another thread wants, for the shortest period of time at all possible.

Of course it's easier to reason about "broad" locks than narrow, so it's worth starting with them broad and optimising as needed, but there are some cases that we learn from experience and familiarity where a narrower fits the pattern.

(Incidentally, if you can possibly just use private static volatile Singleton instance = new Singleton() or if you can possibly just not use singletons but use a static class instead, both are better in regards to these concerns).