Является ли получение и установка простых статических свойств потокобезопасным? [дубликат]

Возможный дубликат:
Являются ли автоматически реализуемые статические свойства C # поточно-ориентированными?

В следующем примере class

static class Shared
{
    public static string[] Values { get; set; }
}

многие потоки чтения периодически читают массив строк Values ​​, в то время как время от времени время от времени один писатель заменит весь массив новым значением с помощью установщика. Нужно ли мне использовать ReaderWriterLock или это то, что C # будет обрабатывать автоматически?

Правка: в моем случае единственная необходимая «безопасность потока»: ничего плохого не должно произойти, когда писатель заменяет массив, пока читатель ищет ценность. Меня не волнует, будут ли читатели использовать новое значение немедленно, если они будут использовать его в будущем

10
задан Community 23 May 2017 в 11:33
поделиться

6 ответов

Это использование потокобезопасно:

string[] localValues = Shared.Values;
for (int index = 0; index < localValues.length; index++)
    ProcessValues(localValues[index]);

Такое использование не является потокобезопасным и может привести к исключениям, выходящим за пределы:

for (int index = 0; index < Shared.Values.Length; index++)
    ProcessValues(Shared.Values[index]);

Я бы предпочел сделать потокобезопасным вызывает более естественный вызов, делая что-то вроде этого:

static class Shared   
{
    private static string[] values;   
    public static string[] GetValues() { return values; }
    public static void SetValues(string[] values) { Shared.values = values; }
}

Конечно, пользователи все еще могут помещать GetValues ​​() в циклы, и это было бы так же плохо, но, по крайней мере, это явно плохо.

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

static class Shared
{
    private static string[] values;
    public static string[] GetValues()
    {
        string[] currentValues = values;
        if (currentValues != null)
            return (string[])currentValues.Clone();
        else
            return null;
    }
    public static void SetValues(string[] values)
    {
        Shared.values = values;
    }
}
10
ответ дан 3 December 2019 в 15:34
поделиться

Я бы заблокировал. Для множественного чтения и случайной записи используйте ReaderWriterLockSlim - гораздо более эффективный, чем ReaderWriteLock .

static class Shared
{
    private static ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
    private static string[] _values;

    public static string[] Values 
    {
        get
        {
            _rwLock.EnterReadLock();
            try
            {
                return _values;
            }
            finally
            {
                _rwLock.ExitReadLock();
            }
        }
        set
        {
            _rwLock.EnterWriteLock();
            try
            {
                _values = value;
            }
            finally
            {
                _rwLock.ExitWriteLock();
            }
        }
    }
}
2
ответ дан 3 December 2019 в 15:34
поделиться

Это не потокобезопасный . Да, вам нужно будет использовать замок.

2
ответ дан 3 December 2019 в 15:34
поделиться

Без дополнительной защиты нет гарантии, что читающий поток когда-либо увидит новое значение. На практике, пока читающий поток выполняет какие-либо значимые действия, он увидит новое значение. В частности, вы никогда не увидите "половинного" обновления, когда ссылка указывает на мертвое пространство.

Если вы сделаете поле волатильным, я полагаю, даже эта опасность будет устранена - но программирование без блокировок вообще трудно поддается рассуждениям. Это означает отказ от автоматически реализуемого свойства, конечно.

8
ответ дан 3 December 2019 в 15:34
поделиться

Это зависит от того, что вы подразумеваете под потокобезопасностью.

Чтение и замена гарантированно будут атомарными, но не гарантируется, что чтение, следующее за записью, обязательно прочитает новое значение.

В ответ на ваше изменение ...

Ничего плохого не произойдет с вашим существующим кодом (например, разорванное чтение), но нет гарантии, что ваши читатели когда-либо увидят новое значение . Например, возможно - хотя и маловероятно - что старая ссылка будет навсегда кэширована в регистре.

5
ответ дан 3 December 2019 в 15:34
поделиться

Чтобы немного отклониться от темы, модель памяти Java 2 1.5 добавляет две гарантии (см. Теория и практика Java: Исправление модели памяти Java, Часть 2 ):

  • энергозависимые операции чтения / записи также являются препятствиями для памяти. Поля
  • final появляются полностью инициализированными вне конструктора.

Последнее представляет собой интересный случай: раньше вы могли выполнять foo = new String (new char [] {'a', 'b', 'c'}); в одном потоке и foo = "123"; System.out.println (foo) в другом потоке и распечатайте пустую строку, потому что не было гарантии, что запись в последние поля foo произойдет до записи в foo.

Я не уверен в деталях инициализации массива .NET; возможно, вам нужно использовать Thread.MemoryBarrier () (в начале получателя и в конце установщика), чтобы читатели видели только полностью инициализированные массивы.

1
ответ дан 3 December 2019 в 15:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: