静态变量线程安全吗?c#
本文关键字:安全 变量 线程 静态 | 更新日期: 2023-09-27 18:04:42
我想创建一个存储DataTables的类,这将防止我的应用程序每次想要检索它时导入详细信息列表。因此,这应该做一次,我相信下面的代码这样做,但我不确定它是否是线程安全的。
下面的代码位于我的三层应用程序的业务层部分,它向表示层返回一个数据表。
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;
}
}
任何帮助是感激的,如果有一个更好的方法如何缓存数据表请让我知道。
更新:感谢你的意见,如果我理解正确的话,这是建议的方法:
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;
}
}
看起来好像您要做的就是加载它一次并保留对它的引用。您需要注意的是,如果变量为空,则对其进行初始化。空检查,锁定和空检查再次被称为Double Check locking ,它将为您工作得很好。最佳实践是提供一个单独的锁定对象,这样您就可以很好地控制锁的粒度。
注意,这并不能阻止人们改变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
的结果中读取,从不写入。
参照http://en.wikipedia.org/wiki/Double-checked_locking修改
我认为值得补充的是,双重检查锁定已经在。net框架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;
}
}
}
它们不是线程安全的。您应该考虑自己使您的逻辑线程安全,例如,通过使用锁操作符。
如果你是在。net 4上,你可以在你的数据表上使用ThreadLocal包装器
有一个很好的链接让你开始:http://en.csharp-online.net/Singleton_design_pattern%3A_Thread-safe_Singleton
除此之外,我强烈建议您使用比遗留的DataTable更现代的方法。查看实体框架或NHibernate。在您的数据层中实现它们将允许您对软件的其余部分隐藏数据库细节,并使其在更高级别的抽象(POCO对象)上工作。我想你会没事的。有一个轻微的机会,两个线程将确定数据表为空,并且都读取表,但是只有一个线程最后分配unitTable
/currencyTable
引用,所以最坏的情况是,您将多次初始化它们。但一旦他们就位,我想你会很好。只要你不给他们写信。这会使人处于不一致的状态。
如果你想避免double init,你可以将整个getter代码包装在lock
语句中。这很像初始化一个单例。
还要添加一个方法,让您将引用再次设置为null,以便您可以强制刷新。
GJ
如果数据表是只读的,那么你应该在填充它们时锁定它们,如果它们从不改变,那么它们将是线程安全的。
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框架中的标题和同步,我建议您参考这个非常广泛的教程http://www.albahari.com/threading/