这个C#代码是否应该重构为使用Lazy <T>类而不是?

Pur*_*ome 8 .net c# lazy-loading double-checked-locking

我有以下代码,可以在同一秒通过多个Web请求调用.因此,我不希望第二个+请求命中数据库,但等到第一个请求命中.

我应该重构这个来使用Lazy<T> 关键字 class吗?如果对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
}
Run Code Online (Sandbox Code Playgroud)

Bro*_*ass 12

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

来自MSDN:

默认情况下,Lazy对象是线程安全的.也就是说,如果构造函数没有指定线程安全的类型,那么它创建的Lazy对象是线程安全的.在多线程方案中,访问线程安全的Lazy对象的Value属性的第一个线程会为所有线程上的所有后续访问初始化它,并且所有线程共享相同的数据.因此,哪个线程初始化对象并且竞争条件是良性的并不重要.

是的,它不是一个关键字 - 它是一个.NET框架类,它通常需要用于延迟初始化的用例,并提供开箱即用的功能,因此您不必"手动"执行此操作.


Bru*_*oLM 8

正如@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();
}
Run Code Online (Sandbox Code Playgroud)

但是,哪一个更快?从结果我得到...

执行代码100次

[   Lazy: 00:00:01.003   ]
[  Field: 00:00:01.000   ]
Run Code Online (Sandbox Code Playgroud)

执行代码100000000次

[   Lazy: 00:00:10.516   ]
[  Field: 00:00:17.969   ]
Run Code Online (Sandbox Code Playgroud)

测试代码:

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

Performance.Test("Field", TestAmount, false,
    () =>
    {
        var laz = FieldInt;
    });
Run Code Online (Sandbox Code Playgroud)

测试方法:

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"));
}
Run Code Online (Sandbox Code Playgroud)