Pregunta

Me encontré con este artículo rel="noreferrer"> href="http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html" discutir por qué el doble -check paradigma de bloqueo se rompe en Java. Es el paradigma válido para .NET (en particular, C #), si las variables se declaran volatile?

¿Fue útil?

Solución

Implementación del patrón Singleton en C # habla de este problema en la tercera versión.

Se dice:

  

Hacer la instancia de variable volátil puede hacer que funcione, como lo haría explícita llama barrera de memoria, aunque en este último caso, incluso los expertos no se ponen de acuerdo exactamente lo que se requieren barreras. Tiendo a tratar de evitar situaciones en las que los expertos no están de acuerdo lo que está bien y lo que está mal!

El autor parece dar a entender es menos probabilidades de trabajar que otras estrategias que cierre doble y por lo tanto no debe ser utilizado.

Otros consejos

Haga doble comprobación de bloqueo ahora trabaja en Java, así como C # (el modelo de memoria de Java cambiado y este es uno de los efectos). Sin embargo, usted tiene que conseguir que exactamente derecha. Si usted complicar las cosas aunque sea ligeramente, que bien puede acabar perdiendo el hilo de seguridad.

Como otras respuestas han dicho, si va a implementar la href="http://pobox.com/~skeet/csharp/singleton.html" rel="noreferrer"> Singleton patrón existe son mucho mejores maneras de hacerlo. En lo personal, si estoy en una situación en la que tengo que elegir entre el bloqueo doble comprobación y "se bloquee cada vez" código me gustaría ir para el bloqueo cada vez hasta que me dieron evidencia real de que estaba causando un cuello de botella. Cuando se trata de enhebrar, un patrón simple y obviamente-correcta vale mucho.

Nota que en Java (y muy probablemente en .Net también), una doble comprobación de bloqueo para la inicialización Singleton es completamente innecesario, así como roto. Dado que las clases no se inicializan hasta que se utilizaron por primera vez, la inicialización perezosa deseada ya se consigue esto;

private static Singleton instance = new Singleton();

A menos que su clase Singleton contiene cosas como constantes que se pueda acceder antes de que se utiliza una instancia de Singleton principio, esto es todo lo que necesita hacer.

No entiendo por qué todo el mundo dice que el bloqueo de doble verificación está mal patrón, pero no se adaptan el código para que funcione correctamente. En mi opinión, este código de abajo debería funcionar bien.

Si alguien me podría decir si este código sufren el problema mencionado en el artículo de Cameron, por favor.

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

    Singleton() {
    }

    public static Singleton Instance {
        get {
            if (instance != null) {
                return instance;
            }

            lock (padlock) {
                if (instance != null) {
                    return instance;
                }

                tempInstance = new Singleton();

                // initialize the object with data

                instance = tempInstance;
            }
            return instance;
        }
    }
}
  

He conseguido una doble comprobación de bloqueo a trabajar mediante el uso de un valor booleano (es decir, usando una primitiva para evitar la inicialización perezoso):

El Singleton usar el booleano no funciona. El orden de las operaciones como se ve entre los diferentes hilos no está garantizada a menos que ir a través de una barrera de memoria. En otras palabras, como se ve desde un segundo hilo, created = true puede ser ejecutado antes de instance= new Singleton();

No acabo de entender por qué hay un montón de patrones de implementación de una doble comprobación de bloqueo (al parecer para evitar la idiosincrasia del compilador en varios idiomas). El artículo de Wikipedia sobre este tema muestra el método ingenuo y las posibles maneras de resolver el problema, pero ninguno es tan simple como esto (en C #):

public class Foo
{
  static Foo _singleton = null;
  static object _singletonLock = new object();

  public static Foo Singleton
  {
    get
    {
      if ( _singleton == null )
        lock ( _singletonLock )
          if ( _singleton == null )
          {
            Foo foo = new Foo();

            // Do possibly lengthy initialization,
            // but make sure the initialization
            // chain doesn't invoke Foo.Singleton.
            foo.Initialize();

            // _singleton remains null until
            // object construction is done.
            _singleton = foo;
          }
      return _singleton;
    }
  }

En Java, se haría uso sincronizado () en lugar del bloqueo (), pero es básicamente la misma idea. Si hay la posible inconsistencia de Singleton cuando el campo se le asigna, entonces ¿por qué no usar una primera de ámbito local variable y luego asignar el campo Singleton en el último momento posible antes de salir de la sección crítica? Me estoy perdiendo algo?

No es el argumento por @ michael-Borgwardt que en C # y Java campo estático sólo se hace una vez inicializado en el primer uso, pero que comportamiento es un lenguaje específico. Y yo he utilizado este patrón de frecuencia para la inicialización perezosa de una propiedad de colección (por ejemplo user.Sessions).

He conseguido una doble comprobación de bloqueo a trabajar mediante el uso de un valor booleano (es decir, usando una primitiva para evitar la inicialización perezoso):

private static Singleton instance;
private static boolean created;
public static Singleton getInstance() {
    if (!created) {
        synchronized (Singleton.class) {
            if (!created) {
                instance = new Singleton();
                created = true;
            }
        }
    }
    return instance;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top