2011-08-24 12 views
7

Mam następujący kod, który może być wywołany przez wiele żądań internetowych w tej samej sekundzie. W związku z tym nie chcę, aby druga próba + trafiała w bazę danych, ale czeka aż pierwsza.Czy ten kod C# powinien być refaktoryzowany, aby używał klasy Lazy <T>?

Czy powinienem to zmienić, aby zamiast tego używać klasy Lazy<T> słów kluczowych ? Jeśli 10 wywołań do kodu Lazy<T> pojawia się w tym samym czasie, wykonaj 9 z tych połączeń, czekając na zakończenie pierwszego?

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 ' nie jest słowem kluczowym. – BoltClock

+0

Więc jak to się nazywa? –

+0

Jest to po prostu typ. Dokładniej, jest to klasa. – BoltClock

Odpowiedz

12

Tak można użyć Lazy<T>

Od MSDN:

Domyślnie Lazy obiekty są thread-safe. Oznacza to, że jeśli konstruktor nie określa rodzaju bezpieczeństwa nici, obiekty Lazy , które utworzyły, są wątkowo bezpieczne. W scenariuszach wielowątkowych pierwszy wątek, który ma uzyskać dostęp do właściwości Wartość obiektu Lazy bezpiecznej dla wątków, inicjuje go dla wszystkich kolejnych dostępów do wszystkich wątków: i wszystkie wątki mają te same dane. Dlatego nie ma znaczenia, który wątek zainicjuje obiekt, a warunki wyścigu są łagodne.

I tak, to nie jest słowo kluczowe - jest to klasa ramowa .NET, która formalizuje często wymagany przypadek użycia do leniwej inicjalizacji i oferuje to po wyjęciu z pudełka, więc nie trzeba tego robić "ręcznie".

8

Jako @BrokenGlass wskazał, że jest bezpieczny. Ale nie mogłem się powstrzymać i musiałem zrobić test ...

tylko jeden identyfikator wątek jest drukowany ...

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

jednak, który z nich jest szybszy? Z wyników dostałem ...

wykonywanie kodu 100 razy

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

wykonywanie kodu 100000000 razy

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

kod Test:

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

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

Metoda badawcza:

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

Niesamowita praca @BrunoLM :) –

Powiązane problemy