I want to create a class which stores DataTables, this will prevent my application to import a list of details each time I want to retrieve it. Therefore this should be done once, I believe that the following code does so, but I am not sure if it is thread-safe.
我想创建一个类,它存储DataTables,这将防止我的应用程序每次要检索它时导入一个详细信息列表。因此,这应该做一次,我相信下面的代码可以做到这一点,但是我不确定它是否线程安全。
The below code is in the Business Layer Section of my three tier application, it is returning a DataTable to the Presentation Layer.
下面的代码位于我的三层应用程序的业务层部分,它将向表示层返回一个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;
}
}
Any help is appreciated, if there is a better way how to cache a DataTable please let me know.
如果有更好的方法缓存数据表,请告诉我。
Update:
更新:
Thanks to your opinions, this is the suggested method to do it, if I understood correctly:
感谢您的意见,如果我理解正确,这是建议的做法:
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;
}
}
7 个解决方案
#1
29
It appears as though all you want to do is load it once and keep a reference to it. All you need to guard is initialising the variable if it's null. Null checking, locking and null checking again is called Double Check Locking and will work well for you. It's best practice to provide a separate locking object, so you have good control over granularity of locks.
看起来您所要做的就是加载它一次并保持对它的引用。您需要注意的是,如果变量为空,则初始化该变量。空检查,锁定和空检查再次被称为双重检查锁定,将会很好地为您工作。提供单独的锁对象是最佳实践,因此您可以很好地控制锁的粒度。
Note this doesn't stop people from mutating the value inside the DataTable
it only stops people from trying to initialise the static member at the same time.
注意,这并不能阻止人们改变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;
}
Only read from the result of GetUnitList
never write to it.
只从GetUnitList的结果中读取,而不是写入它。
Amended with reference to http://en.wikipedia.org/wiki/Double-checked_locking
修改为http://en.wikipedia.org/wiki/double checked_locked
#2
13
I thought it would be worth adding that Double Check Locking has since been implemented in .net framework 4.0 in a class named Lazy
. So if you would like your class to include the locking by default then you can use it like this:
我认为值得添加的是,在一个名为Lazy的类中,.net framework 4.0中已经实现了双重检查锁定。如果你想让你的类包含默认的锁定那么你可以这样使用它:
public class MySingleton
{
private static readonly Lazy<MySingleton> _mySingleton = new Lazy<MySingleton>(() => new MySingleton());
private MySingleton() { }
public static MySingleton Instance
{
get
{
return _mySingleton.Value;
}
}
}
#3
7
They are not thread safe. You should think about making your logic thread safe by your self, for example, by using lock operator.
它们不是线程安全的。您应该考虑通过使用lock运算符使逻辑线程安全。
#4
5
If you are on .net 4 you could use ThreadLocal wrappers on your datatables
如果您在。net 4上,您可以在您的datatable上使用ThreadLocal包装器
#5
2
Static variables aren't thread safe per-se. You should design with thread safety in mind.
静态变量本身并不是线程安全的。您应该在设计时考虑到线程的安全性。
There's a good link to get you started: http://en.csharp-online.net/Singleton_design_pattern%3A_Thread-safe_Singleton
有一个很好的链接可以让你开始:http://en.csharp-online.net/Singleton_design_pattern%3A_Thread-safe_Singleton
Apart from this, I would strongly recommend you to use a more modern approach than the legacy DataTable. Check out the Entity Framework or NHibernate. Implementing them in your datalayer will allow you to hide database details from the rest of the software and let it work on a higher level abstraction (POCO objects).
除此之外,我强烈建议您使用比遗留数据表更现代的方法。检查实体框架或NHibernate。在datalayer中实现它们将允许您对软件的其他部分隐藏数据库细节,并允许它在更高层次的抽象(POCO对象)上工作。
#6
1
I think you should be fine. There is a liight chance that 2 threads will determine that the datatable is null and both read the table, but only one gets to assign the unitTable
/ currencyTable
reference last, so worst case you be initalizing them more than once. But once they're set I think you'd be good. AS LONG AS YOU DON'T WRITE TO THEM. Theat could leave one in an inconsistent state.
我想你应该没事。有可能有两个线程会确定datatable为null并同时读取表,但只有一个线程会将unitTable / currencyTable引用分配到最后,因此最坏的情况是,您将它们进行多次调整。但一旦他们确定了,我认为你会很好。只要你不给他们写信。Theat可能会让一个人处于不一致的状态。
If you want to avoid the double init you could wrap the whole getter code in a lock
statement. It's a lot like initializing a singleton.
如果您想避免双初始化,可以将整个getter代码封装到一个lock语句中。这很像初始化一个单例。
Also add a method that let's you set the references to null again so you can force a refresh.
还可以添加一个方法,让我们再次将引用设置为null,以便强制刷新。
GJ
GJ
#7
1
If the DataTables are read-only then you should lock them when you populate them and if they never change then they will be thread safe.
如果这些数据是只读的,那么在填充它们时应该锁定它们,如果它们从不更改,那么它们将是线程安全的。
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;
}
}
If you need really high performance on this lookup you can use the ReaderWriterLockSlim class instead of a full lock everytime to limit the number of waits that will happen in the application.
如果您需要在这个查找上有很高的性能,您可以使用ReaderWriterLockSlim类,而不是每次都使用全锁来限制应用程序中等待的数量。
Check out http://kenegozi.com/blog/2010/08/15/readerwriterlockslim-vs-lock for a short article on the differences between lock and ReaderWriterLockSlim
查看http://kenegozi.com/blog/20108/15 / ReaderWriterLockSlim -vs-lock,获得一篇关于lock和ReaderWriterLockSlim之间区别的短文
EDIT: (Answer to comments below)
编辑:(回答下面的评论)
The unitTableLock object is used like a handle for the Monitor class in to synchronize against.
unitTableLock对象就像用于同步的监视器类的句柄。
For a full overview of Theading and synchronization in the .NET framework I would point you over to this very extensive tutorial http://www.albahari.com/threading/
为了全面概述。net框架中的读取和同步,我将向您介绍这个非常广泛的教程http://www.albahari.com/threading/
#1
29
It appears as though all you want to do is load it once and keep a reference to it. All you need to guard is initialising the variable if it's null. Null checking, locking and null checking again is called Double Check Locking and will work well for you. It's best practice to provide a separate locking object, so you have good control over granularity of locks.
看起来您所要做的就是加载它一次并保持对它的引用。您需要注意的是,如果变量为空,则初始化该变量。空检查,锁定和空检查再次被称为双重检查锁定,将会很好地为您工作。提供单独的锁对象是最佳实践,因此您可以很好地控制锁的粒度。
Note this doesn't stop people from mutating the value inside the DataTable
it only stops people from trying to initialise the static member at the same time.
注意,这并不能阻止人们改变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;
}
Only read from the result of GetUnitList
never write to it.
只从GetUnitList的结果中读取,而不是写入它。
Amended with reference to http://en.wikipedia.org/wiki/Double-checked_locking
修改为http://en.wikipedia.org/wiki/double checked_locked
#2
13
I thought it would be worth adding that Double Check Locking has since been implemented in .net framework 4.0 in a class named Lazy
. So if you would like your class to include the locking by default then you can use it like this:
我认为值得添加的是,在一个名为Lazy的类中,.net framework 4.0中已经实现了双重检查锁定。如果你想让你的类包含默认的锁定那么你可以这样使用它:
public class MySingleton
{
private static readonly Lazy<MySingleton> _mySingleton = new Lazy<MySingleton>(() => new MySingleton());
private MySingleton() { }
public static MySingleton Instance
{
get
{
return _mySingleton.Value;
}
}
}
#3
7
They are not thread safe. You should think about making your logic thread safe by your self, for example, by using lock operator.
它们不是线程安全的。您应该考虑通过使用lock运算符使逻辑线程安全。
#4
5
If you are on .net 4 you could use ThreadLocal wrappers on your datatables
如果您在。net 4上,您可以在您的datatable上使用ThreadLocal包装器
#5
2
Static variables aren't thread safe per-se. You should design with thread safety in mind.
静态变量本身并不是线程安全的。您应该在设计时考虑到线程的安全性。
There's a good link to get you started: http://en.csharp-online.net/Singleton_design_pattern%3A_Thread-safe_Singleton
有一个很好的链接可以让你开始:http://en.csharp-online.net/Singleton_design_pattern%3A_Thread-safe_Singleton
Apart from this, I would strongly recommend you to use a more modern approach than the legacy DataTable. Check out the Entity Framework or NHibernate. Implementing them in your datalayer will allow you to hide database details from the rest of the software and let it work on a higher level abstraction (POCO objects).
除此之外,我强烈建议您使用比遗留数据表更现代的方法。检查实体框架或NHibernate。在datalayer中实现它们将允许您对软件的其他部分隐藏数据库细节,并允许它在更高层次的抽象(POCO对象)上工作。
#6
1
I think you should be fine. There is a liight chance that 2 threads will determine that the datatable is null and both read the table, but only one gets to assign the unitTable
/ currencyTable
reference last, so worst case you be initalizing them more than once. But once they're set I think you'd be good. AS LONG AS YOU DON'T WRITE TO THEM. Theat could leave one in an inconsistent state.
我想你应该没事。有可能有两个线程会确定datatable为null并同时读取表,但只有一个线程会将unitTable / currencyTable引用分配到最后,因此最坏的情况是,您将它们进行多次调整。但一旦他们确定了,我认为你会很好。只要你不给他们写信。Theat可能会让一个人处于不一致的状态。
If you want to avoid the double init you could wrap the whole getter code in a lock
statement. It's a lot like initializing a singleton.
如果您想避免双初始化,可以将整个getter代码封装到一个lock语句中。这很像初始化一个单例。
Also add a method that let's you set the references to null again so you can force a refresh.
还可以添加一个方法,让我们再次将引用设置为null,以便强制刷新。
GJ
GJ
#7
1
If the DataTables are read-only then you should lock them when you populate them and if they never change then they will be thread safe.
如果这些数据是只读的,那么在填充它们时应该锁定它们,如果它们从不更改,那么它们将是线程安全的。
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;
}
}
If you need really high performance on this lookup you can use the ReaderWriterLockSlim class instead of a full lock everytime to limit the number of waits that will happen in the application.
如果您需要在这个查找上有很高的性能,您可以使用ReaderWriterLockSlim类,而不是每次都使用全锁来限制应用程序中等待的数量。
Check out http://kenegozi.com/blog/2010/08/15/readerwriterlockslim-vs-lock for a short article on the differences between lock and ReaderWriterLockSlim
查看http://kenegozi.com/blog/20108/15 / ReaderWriterLockSlim -vs-lock,获得一篇关于lock和ReaderWriterLockSlim之间区别的短文
EDIT: (Answer to comments below)
编辑:(回答下面的评论)
The unitTableLock object is used like a handle for the Monitor class in to synchronize against.
unitTableLock对象就像用于同步的监视器类的句柄。
For a full overview of Theading and synchronization in the .NET framework I would point you over to this very extensive tutorial http://www.albahari.com/threading/
为了全面概述。net框架中的读取和同步,我将向您介绍这个非常广泛的教程http://www.albahari.com/threading/