2011-08-24 55 views
7

我有下面的代碼,可以通過多個Web請求在同一秒調用。因此,我不希望第二個請求觸及數據庫,但是一直等到第一個請求完成。應該重構此C#代碼以使用Lazy <T>類嗎?

我應該重構這個使用Lazy<T> 關鍵字 類嗎?如果同時發生對Lazy<T>一段代碼的10次調用,那麼這9次調用是否等待第一次調用完成?

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

'懶惰'不是關鍵字。 – BoltClock

+0

那麼它叫什麼呢? –

+0

它簡單地稱爲一種類型。更具體地說,這是一個班級。 – BoltClock

回答

12

是的,你可以使用Lazy<T>

MSDN

默認情況下,懶惰的對象是線程安全的。也就是說,如果 構造函數沒有指定線程安全類型,則它創建的Lazy 對象是線程安全的。在多線程場景中,訪問線程安全的Lazy 對象的Value屬性的第一個線程會將其初始化爲所有線程上的所有後續訪問和 所有線程共享相同的數據。因此,線程初始化哪個對象並不重要,競態條件是良性的。

是的,它不是一個關鍵字 - 它是正式確定延遲初始化經常需要使用的情況下,提供這種開箱即用的,所以你不必做「人工」 .NET Framework類。

8

As @BrokenGlass指出它是安全的。但我無法抗拒,不得不進行測試......

只有一個線程ID印...

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

但是,哪一個是更快?從結果來看我......

執行代碼100倍

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

執行代碼億次

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

測試代碼:

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

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

測試方法:

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

真棒工作@BrunoLM :) –