Pregunta

I have the following code which could be called via multiple web-requests at the same second. As such, I don't want the second+ request hitting the database, but waiting until the first one does.

Should I refactor this to use the Lazy<T> keyword class instead? If 10 calls to a Lazy<T> piece of code occur at the same time, do 9 of those calls wait for the first one to complete?

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
}
¿Fue útil?

Solución

Yes you can use Lazy<T>

From MSDN:

By default, Lazy objects are thread-safe. That is, if the constructor does not specify the kind of thread safety, the Lazy objects it creates are thread-safe. In multithreaded scenarios, the first thread to access the Value property of a thread-safe Lazy object initializes it for all subsequent accesses on all threads, and all threads share the same data. Therefore, it does not matter which thread initializes the object, and race conditions are benign.

And yes, it's not a keyword - its a .NET framework class that formalizes the often required use case for lazy initialization and offers this out of the box so you don't have to do it "manually".

Otros consejos

As @BrokenGlass pointed out it is safe. But I couldn't resist and had to make a test...

Only one thread id is printed...

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

But, which one is faster? From the results I got...

Executing the code 100 times

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

Executing the code 100000000 times

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

Test code:

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

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

Test method:

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"));
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top