Являются ли статические переменные потокобезопасными? С#
Я хочу создать класс, в котором хранятся DataTables, это предотвратит мое приложение для импорта списка деталей каждый раз, когда я хочу его получить. Поэтому это нужно сделать один раз, я считаю, что следующий код делает это, но я не уверен, что он потокобезопасен.
Нижеприведенный код находится в разделе Business 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;
}
}
Любая помощь приветствуется, если есть лучший способ кэширования DataTable, пожалуйста, дайте мне знать.
Update:
Благодаря вашим мнениям, это предложенный метод для этого, если я правильно понял:
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;
}
}
Ответы
Ответ 1
Кажется, что все, что вы хотите сделать, это загрузить его один раз и сохранить ссылку на него. Все, что вам нужно для защиты, это инициализировать переменную, если она равна нулю. Нулевая проверка, блокировка и проверка нуля снова называются 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
Ответ 2
Я подумал, что стоит добавить, что 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;
}
}
}
Ответ 3
Они не являются потокобезопасными. Вы должны подумать о том, чтобы сделать ваш логический поток безопасным для себя, например, с помощью оператора блокировки.
Ответ 4
Если вы находитесь в .net 4, вы можете использовать ThreadLocal обертки для ваших данных
Ответ 5
Статические переменные не являются потокобезопасными per se. Вы должны проектировать с учетом безопасности потока.
Есть хорошая ссылка, чтобы вы начали: http://en.csharp-online.net/Singleton_design_pattern%3A_Thread-safe_Singleton
Кроме того, я настоятельно рекомендую вам использовать более современный подход, чем устаревший DataTable. Проверьте структуру Entity Framework или NHibernate. Реализация их в вашем datalayer позволит вам скрыть данные базы данных от остальной части программного обеспечения и позволить ей работать с абстракцией более высокого уровня (объекты POCO).
Ответ 6
Думаю, с тобой все в порядке. Существует вероятность того, что 2 потока определит, что datatable является нулевым, и оба читают таблицу, но только один получает привязку ссылки unitTable
/currencyTable
, поэтому в худшем случае вы их инициализируете более одного раза. Но как только они будут установлены, я думаю, что вы будете хороши. КАК ДОЛГО, КАК ВЫ НЕ ПИШИТЕ ИХ. Теат мог оставить его в противоречивом состоянии.
Если вы хотите избежать двойного init, вы можете обернуть весь код getter в инструкции lock
. Это очень похоже на инициализацию синглета.
Также добавьте метод, который позволит вам снова установить ссылки на null, чтобы вы могли принудительно обновить.
ГДж
Ответ 7
Если таблицы данных доступны только для чтения, тогда вы должны блокировать их, когда вы заполняете их, и если они никогда не меняются, они будут потокобезопасными.
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 за короткую статью о различиях между блокировкой и ReaderWriterLockSlim
EDIT: (ответьте на комментарии ниже)
Объект unitTableLock используется как дескриптор для класса Monitor для синхронизации.
Для полного обзора Theading и синхронизации в платформе .NET я хотел бы указать вам на этот очень обширный учебник http://www.albahari.com/threading/