我想创建一个存储DataTables的类,这会阻止我的应用程序在每次要检索它时导入详细信息列表。因此,这应该做一次,我相信下面的代码这样做,但我不确定它是否是线程安全的。
以下代码位于我的三层应用程序的业务层部分,它将DataTable返回到表示层。
public class BusinessLayerHandler
{
public static DataTable unitTable;
public static DataTable currencyTable;
public static DataTable GetUnitList()
{
//import lists each time the application is run
unitTable = null;
if (unitTable == null)
{
return unitTable = DatabaseHandler.GetUnitList();
}
else
{
return unitTable;
}
}
public static DataTable GetCurrencyList()
{
//import lists each time the application is run
currencyTable = null;
if (currencyTable == null)
{
return currencyTable = DatabaseHandler.GetCurrencyList();
}
else
{
return currencyTable;
}
}
如果有更好的方法来缓存DataTable,请提供任何帮助,请告知我们。
更新
感谢您的意见,如果我理解正确的话,这是建议的方法:
public class BusinessLayerHandler
{
private static DataTable unitTable;
private static DataTable currencyTable;
private static readonly object unitTableLock = new object();
private static readonly object currencyTableLock = new object();
public static DataTable GetUnitList()
{
//import lists each time the application is run
//unitTable = null;
lock (unitTableLock)
{
if (unitTable == null)
{
return unitTable = DatabaseHandler.GetUnitList();
}
}
return unitTable;
}
public static DataTable GetCurrencyList()
{
//import lists each time the application is run
lock (currencyTableLock)
{
if (currencyTable == null)
{
return currencyTable = DatabaseHandler.GetCurrencyList();
}
}
return currencyTable;
}
}
答案 0 :(得分:32)
好像你想要做的就是加载一次并保留对它的引用。您需要保护的只是初始化变量(如果它为空)。空检查,锁定和空检查再次被称为双重检查锁定,并且适合您。最佳做法是提供单独的锁定对象,以便您可以很好地控制锁的粒度。
请注意,这不会阻止人们改变DataTable
中的值,它只会阻止人们同时尝试初始化静态成员。
private static readonly object UnitTableLock = new object();
private static DataTable unitTable;
private static bool _ready = false;
public static DataTable GetUnitList()
{
if (!_ready)
{
lock (UnitTableLock)
{
if (!_ready)
{
unitTable = new DataTable; //... etc
System.Threading.Thread.MemoryBarrier();
_ready = true;
}
}
}
return unitTable;
}
只读取GetUnitList
的结果从不写入。
答案 1 :(得分:16)
我认为值得补充的是,Double Check Locking已经在.net framework 4.0中的一个名为Lazy
的类中实现。因此,如果您希望您的类默认包含锁定,那么您可以像这样使用它:
public class MySingleton
{
private static readonly Lazy<MySingleton> _mySingleton = new Lazy<MySingleton>(() => new MySingleton());
private MySingleton() { }
public static MySingleton Instance
{
get
{
return _mySingleton.Value;
}
}
}
答案 2 :(得分:8)
它们不是线程安全的。您应该考虑自己使逻辑线程安全,例如,使用锁定运算符。
答案 3 :(得分:6)
如果您使用的是.net 4,则可以在数据表上使用ThreadLocal包装器
答案 4 :(得分:2)
静态变量本身不是线程安全的。您应该考虑到线程安全性。
有一个很好的链接可以帮助您入门:http://en.csharp-online.net/Singleton_design_pattern%3A_Thread-safe_Singleton
除此之外,我强烈建议您使用比传统DataTable更现代的方法。查看实体框架或NHibernate。在数据层中实现它们将允许您从其余软件隐藏数据库详细信息,并让它在更高级别的抽象(POCO对象)上工作。
答案 5 :(得分:2)
我认为你应该没问题。 2个线程很可能确定数据表为空并且都读取了表,但只有一个线程最后分配unitTable
/ currencyTable
引用,所以最坏的情况是你将它们更多地转化为更多不止一次。但是一旦他们确定了,我认为你会很好。因为你没有写信给他们。 Theat可能让一个处于不一致的状态。
如果要避免使用double init,可以将整个getter代码包装在lock
语句中。这很像初始化单身人士。
还添加一个方法,让您再次将引用设置为null,以便强制刷新。
GJ
答案 6 :(得分:1)
如果DataTables是只读的,那么你应该在填充它们时锁定它们,如果它们永远不会改变那么它们将是线程安全的。
public class BusinessLayerHandler
{
public static DataTable unitTable;
public static DataTable currencyTable;
private static readonly object unitTableLock = new object();
private static readonly object currencyTableLock = new object();
public static DataTable GetUnitList()
{
//import lists each time the application is run
lock(unitTableLock)
{
if (unitTable == null)
{
unitTable = DatabaseHandler.GetUnitList();
}
}
return unitTable;
}
public static DataTable GetCurrencyList()
{
//import lists each time the application is run
lock(currencyTableLock)
{
if (currencyTable == null)
{
currencyTable = DatabaseHandler.GetCurrencyList();
}
}
return currencyTable;
}
}
如果您在此查找中需要非常高的性能,则可以使用ReaderWriterLockSlim类而不是每次完全锁定来限制应用程序中发生的等待次数。
查看http://kenegozi.com/blog/2010/08/15/readerwriterlockslim-vs-lock以获取有关lock和ReaderWriterLockSlim之间差异的简短文章
编辑:(回答下面的评论)
unitTableLock对象的使用类似于Monitor类的句柄来同步。
有关.NET框架中的Theading和同步的完整概述,我将向您指出这个非常广泛的教程http://www.albahari.com/threading/