Подтвердить что ты не робот

Являются ли статические переменные потокобезопасными? С#

Я хочу создать класс, в котором хранятся 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;
    }
}
4b9b3361

Ответ 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/