C # поток безопасности глобальных настроек конфигурации - PullRequest
6 голосов
/ 28 мая 2009

В приложении C # предположим, что у меня есть один глобальный класс, который содержит некоторые элементы конфигурации, например:

public class Options  
{  
    int myConfigInt;  
    string myConfigString;  
    ..etc.  
}  

static Options GlobalOptions;  

члены этого класса будут использоваться в разных потоках:

Thread1: GlobalOptions.myConfigString = blah;

пока

Thread2: string thingie = GlobalOptions.myConfigString;

Использование блокировки для доступа к объекту GlobalOptions также приведет к ненужному блокированию, когда 2 потока обращаются к разным элементам, но, с другой стороны, создание объекта синхронизации для каждого элемента также кажется чрезмерным.

Кроме того, использование блокировки глобальных опций сделало бы мой код менее приятным; если я должен написать

string stringiwanttouse;
lock(GlobalOptions)
{
   stringiwanttouse = GlobalOptions.myConfigString;
}

везде (и является ли этот потокобезопасным или теперь stringiwanttouse просто указателем на myConfigString? Да, я новичок в C # ....) вместо

string stringiwanttouse = GlobalOptions.myConfigString;

код выглядит ужасно.

Итак ... Какой самый лучший (и самый простой!) Способ обеспечить безопасность потоков?

Ответы [ 4 ]

4 голосов
/ 28 мая 2009

Вы можете обернуть соответствующее поле (в данном случае myConfigString) в свойстве и иметь код в Get / Set, который использует либо Monitor.Lock, либо Mutex. Затем доступ к свойству блокирует только это поле и не блокирует весь класс.

Редактировать: добавление кода

private static object obj = new object(); // only used for locking
public static string MyConfigString {
    get {
       lock(obj)
       {
          return myConfigstring;
       }
    }
    set {
       lock(obj)
       {
          myConfigstring = value;
       }
    }
}
3 голосов
/ 28 мая 2009

Перед редактированием ОП было написано следующее:

public static class Options
{
    private static int _myConfigInt;
    private static string _myConfigString;

    private static bool _initialized = false;
    private static object _locker = new object();

    private static void InitializeIfNeeded()
    {
        if (!_initialized) {
            lock (_locker) {
                if (!_initialized) {
                    ReadConfiguration();
                    _initalized = true;
                }
            }
        }
    }

    private static void ReadConfiguration() { // ... }

    public static int MyConfigInt {
        get {
            InitializeIfNeeded();
            return _myConfigInt;
        }
    }

    public static string MyConfigString {
        get {
            InitializeIfNeeded();
            return _myConfigstring;
        }
    }
    //..etc. 
}

После этого редактирования я могу сказать, что вы должны сделать что-то подобное вышеописанному и настроить конфигурацию только в одном месте - классе конфигурации. Таким образом, это будет единственный класс, изменяющий конфигурацию во время выполнения, и только тогда, когда необходимо получить параметр конфигурации.

0 голосов
/ 28 мая 2009

Что вы подразумеваете под безопасностью потоков здесь? Это не глобальный объект, который должен быть потокобезопасным, это код доступа. Если два потока пишут в переменную-член в одно и то же время, один из них «победит», но это проблема? Если ваш клиентский код зависит от того, что глобальное значение остается постоянным до тех пор, пока оно не будет выполнено с какой-либо единицей обработки, вам потребуется создать объект синхронизации для каждого свойства, которое необходимо заблокировать. Там нет никакого отличного способа обойти это. Вы можете просто кэшировать локальную копию значения, чтобы избежать проблем, но применимость этого исправления будет зависеть от ваших обстоятельств. Кроме того, я бы не стал создавать объект синхронизации для каждого свойства по умолчанию, но вместо этого, как вы понимаете, он вам понадобится.

0 голосов
/ 28 мая 2009

Ваши конфигурации могут быть «глобальными», но они должны не быть представленными как глобальная переменная. Если конфигурации не меняются, их следует использовать для создания объектов, которые нуждаются в информации - либо вручную, либо через фабричный объект. Если они могут измениться, то следует использовать объект, который просматривает файл конфигурации / базы данных / что угодно и реализует шаблон Observer .

Глобальные переменные (даже те, которые являются экземпляром класса) являются плохой вещью & trade;

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...