2011-08-24 6 views
7

Ho il seguente codice che potrebbe essere chiamato tramite più richieste web nello stesso secondo. Come tale, non voglio che la seconda + richiesta colpisca il database, ma attendo fino a quando il primo non lo fa.Questo codice C# dovrebbe essere refactored per utilizzare invece la classe Lazy <T>?

Devo refactor questo per utilizzare invece la classe Lazy<T> parola chiave ? Se 10 chiamate a una parte di codice Lazy<T> si verificano allo stesso tempo, 9 di queste chiamate attendono il completamento della prima?

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 ' non è una parola chiave. – BoltClock

+0

Allora come si chiama? –

+0

Si chiama semplicemente un tipo. Più specificamente, è una classe. – BoltClock

risposta

12

Sì, è possibile utilizzare Lazy<T>

Da MSDN:

Per impostazione predefinita, gli oggetti pigri sono thread-safe. Cioè, se il costruttore non specifica il tipo di sicurezza del thread, gli oggetti Lazy creati sono thread-safe. Negli scenari multithreading, il primo thread per accedere alla proprietà Value di un oggetto Lazy Lazy thread-safe inizializza per tutti gli accessi successivi su tutti i thread e tutti i thread condividono gli stessi dati. Pertanto, non importa quale thread inizializza l'oggetto e le condizioni di gara sono benigne.

E sì, non è una parola chiave - la sua una classe .NET Framework che formalizza il caso d'uso spesso richiesto per l'inizializzazione pigra e offre questo fuori dalla scatola in modo che non hanno a che fare "manualmente".

8

Come @BrokenGlass ha sottolineato che è sicuro. Ma non ho potuto resistere e ha dovuto fare una prova ...

Solo un thread id viene stampato ...

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(); 
} 

Ma, quale è più veloce? Dai risultati che ho ottenuto ...

eseguire il codice 100 volte

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

l'esecuzione del codice 100000000 volte

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

Codice di prova:

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

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

Metodo di prova:

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

Impressionante lavoro @BrunoLM :) –