Comunidad orientada al desarrollo de videojuegos

Unity3D Patrón Singleton Thread Safe

En el anterior articulo Unity3D Patrón Singleton , hemos hablado de como implementar el patrón Singleton de una manera bastante sencilla. En la mayoría de los casos, no necesitaremos hacer nada mas, pero si nuestro juego usa hilos, podríamos tener un problema si ambos hilos intentan acceder a la instancia. Como comentamos en el anterior post, queremos que nuestro Singleton sea un MonoBehaviour para que se comporte como un script normal de Unity y podamos acceder a las Coroutines.

Si partimos del Script anterior:

public class MySingleton : MonoBehaviour
{
    private static MySingleton instance;

    public static MySingleton Instance
    {
        get
        {
            if (instance == null)
            {
                GameObject mySingletonObject = new GameObject("MySingletonObject");
                DontDestroyOnLoad(mySingletonObject);
                instance = mySingletonObject.AddComponent<MySingleton>();
            }
            return instance;
        }
    }
}
Static Initialization

Para hacer que sea Thread Safe de la manera mas sencilla, ya que no podemos hacer news de MonoBahaviour, sería sacar la inicialización de la instancia a un método static a parte.

public class MySingleton : MonoBehaviour
{
    private static readonly MySingleton instance = MySingletonInicialize();

    public static MySingleton Instance
    {
        get
        {
            return instance;
        }
    }

    private static MySingleton MySingletonInicialize()
    {
        Debug.Log("Me inicializo!");
        GameObject mySingletonObject = new GameObject("MySingletonObject");
        DontDestroyOnLoad(mySingletonObject);
        return mySingletonObject.AddComponent<MySingleton>();
    }
}

Este método es valido y Thread Safe pero perderíamos el Lazy instantiation y simplemente con crear una referencia a nuestro Singleton ya estaríamos inicializándolo, en lugar de inicializarlo únicamente cuando lo vamos a usar.

typeof(MySingleton)

Con la línea de aquí arriba ya se lanzaría la inicialización del Singleton y no necesitamos acceder a el aun, así que vamos a ver otra forma, un poco mas compleja pero soluciona los pequeños problemas que nos puede generar el método anterior.

Double-check locking

Se llama doble check, porque vamos a hacer una comprobación de si el Singleton esta inicializado una primera vez, después vamos a bloquear el recurso para que nadie mas acceda a el y una vez bloqueado, vamos a volver a comprobar que el Singleton no se ha inicializado. De esta forma, nos aseguramos que no se ha inicializado mientras estábamos bloqueando el Singleton.

public class MySingletonLock : MonoBehaviour
{
    private static volatile MySingletonLock instance;
    private static object _objToLock = new Object();

    public static MySingletonLock Instance
    {
        get
        {
            if (instance == null)
            {
                lock (_objToLock)
                {
                    if (instance == null)
                        instance = MySingletonInicialize();
                }
            }
            return instance;
        }
    }

    private static MySingletonLock MySingletonInicialize()
    {
        Debug.Log("Me inicializo!");
        GameObject mySingletonObject = new GameObject("MySingletonObject");
        DontDestroyOnLoad(mySingletonObject);
        return mySingletonObject.AddComponent<MySingletonLock>();
    }
}

Como podéis ver, hemos creado un object únicamente para hacer el lock, comprobamos que no este creada la instancia del Singleton antes y después de hacer el lock. También hemos añadido la palabra clave volatile a la instancia del Singleton para asegurarnos de que todos los procesadores acceden a los mismo datos al mismo tiempo.

Muchas gracias a @diosenlatierra por su ayuda.

Os dejo el código en el que podréis comprobar como la inicialización con MySingleton se hace al hacer una referencia a MySingleton y con MySingletonLock se hace lazy instantiation, solo se crea cuando accedemos a la instancia del Singleton.

SingletonThreadSafe.unitypackage

Un saludo!

, , , ,

Leave a Reply