2011-08-24 6 views
7

Tengo el siguiente código que podría llamarse a través de múltiples solicitudes web en el mismo segundo. Como tal, no quiero que la segunda solicitud + llegue a la base de datos, sino que espere hasta que la primera lo haga.¿Debería refactorizarse este código C# para usar la clase Lazy <T>?

¿Debo refactorizar esto para usar la clase de palabra clave Lazy<T> en su lugar? Si se producen 10 llamadas a un código Lazy<T> al mismo tiempo, ¿esperan 9 de esas llamadas a que se complete la primera?

public class ThemeService : IThemeService 
{ 
    private static readonly object SyncLock = new object(); 
    private static IList<Theme> _themes; 
    private readonly IRepository<Theme> _themeRepository; 

    <snip snip snip> 

    #region Implementation of IThemeService 

    public IList<Theme> Find() 
    { 
     if (_themes == null) 
     { 
      lock (SyncLock) 
      { 
       if (_themes == null) 
       { 
        // Load all the themes from the Db. 
        _themes = _themeRepository.Find().ToList(); 
       } 
      } 
     } 

     return _themes; 
    } 

    <sip snip snip> 

    #endregion 
} 
+2

'Lazy ' no es una palabra clave. – BoltClock

+0

Entonces, ¿cómo se llama, entonces? –

+0

Simplemente se llama un tipo. Más específicamente, es una clase. – BoltClock

Respuesta

12

Sí, puedes usar Lazy<T>

De MSDN:

Por defecto, los objetos son perezosos flujos seguros. Es decir, si el constructor no especifica el tipo de seguridad de subprocesos, los objetos Lazy que crea son seguros para subprocesos. En los escenarios multiproceso, el primer subproceso para acceder a la propiedad Value de un objeto Lazy seguro para subprocesos lo inicializa para todos los accesos posteriores en todos los subprocesos y todos los subprocesos comparten los mismos datos. Por lo tanto, no importa qué subproceso inicializa el objeto, y las condiciones de carrera son benignas.

Y sí, no es una palabra clave - es una clase de .NET Framework que formaliza el caso de uso a menudo requerido para la inicialización perezosa y ofrece esta fuera de la caja por lo que no tiene que hacerlo "manualmente".

8

Como @BrokenGlass señaló que es seguro. Pero no pude resistir y tuvo que hacer una prueba ...

Sólo se imprime una ID del tema ...

private static Lazy<int> lazyInt; 

// make it slow 
private int fib() 
{ 
    Thread.Sleep(1000); 
    return 0; 
} 

public void Test() 
{ 
    // when run prints the thread id 
    lazyInt = new Lazy<int>(
     () => 
     { 
      Debug.WriteLine("ID: {0} ", Thread.CurrentThread.ManagedThreadId); 
      return fib(); 
     }); 

    var t1 = new Thread(() => { var x = lazyInt.Value; }); 
    var t2 = new Thread(() => { var x = lazyInt.Value; }); 
    var t3 = new Thread(() => { var x = lazyInt.Value; }); 

    t1.Start(); 
    t2.Start(); 
    t3.Start(); 

    t1.Join(); 
    t2.Join(); 
    t3.Join(); 
} 

Pero, ¿cuál es más rápido? A partir de los resultados que obtuve ...

ejecutar el código 100 veces

[ Lazy: 00:00:01.003 ] 
[ Field: 00:00:01.000 ] 

ejecutar el código 100000000 veces

[ Lazy: 00:00:10.516 ] 
[ Field: 00:00:17.969 ] 

Código de ensayo:

Performance.Test("Lazy", TestAmount, false, 
    () => 
    { 
     var laz = lazyInt.Value; 
    }); 

Performance.Test("Field", TestAmount, false, 
    () => 
    { 
     var laz = FieldInt; 
    }); 

Método de ensayo:

public static void Test(string name, decimal times, bool precompile, Action fn) 
{ 
    if (precompile) 
    { 
     fn(); 
    } 

    GC.Collect(); 
    Thread.Sleep(2000); 

    var sw = new Stopwatch(); 

    sw.Start(); 

    for (decimal i = 0; i < times; ++i) 
    { 
     fn(); 
    } 

    sw.Stop(); 

    Console.WriteLine("[{0,15}: {1,-15}]", name, new DateTime(sw.Elapsed.Ticks).ToString("HH:mm:ss.fff")); 
} 
+0

Awesome work @BrunoLM :) –

Cuestiones relacionadas