我有以下代码,可以在同一秒通过多个Web请求调用。因此,我不希望第二个+请求命中数据库,但等到第一个请求命中。
我应该重构这个以使用Lazy<T>
关键字类吗?如果同时发生10次Lazy<T>
段代码调用,那么其中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
}
答案 0 :(得分:12)
是的,您可以使用Lazy<T>
来自MSDN:
默认情况下,Lazy对象是线程安全的。也就是说,如果 构造函数没有指定线程安全的种类,Lazy 它创建的对象是线程安全的。在多线程场景中, 第一个线程访问线程安全的Lazy的Value属性 对象初始化它以用于所有线程上的所有后续访问,和 所有线程共享相同的数据。因此,哪个无关紧要 线程初始化对象,竞争条件是良性的。
是的,它不是一个关键字 - 它是一个.NET框架类,它规范了懒惰初始化经常需要的用例并提供了开箱即用的功能,所以你不必这样做& #34;手动&#34;
答案 1 :(得分: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();
}
但是哪一个更快?从我得到的结果......
执行代码100次
[ Lazy: 00:00:01.003 ]
[ Field: 00:00:01.000 ]
执行代码100000000次
[ 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"));
}