Летучие поля в C # - PullRequest
       96

Летучие поля в C #

11 голосов
/ 25 февраля 2011

Из спецификации 10.5.3 Летучие поля:


Тип изменчивого поля должен быть одним из следующих:

  • Тип ссылки.

  • Тип byte, sbyte, short, ushort, int, uint, char, float, bool, System.IntPtr или System.UIntPtr.

  • Тип enum с базовым типом enum байт, sbyte, короткий, ushort, int, или уинт.


Сначала я хочу подтвердить, что мое понимание верно: я предполагаю, что вышеупомянутые типы могут быть энергозависимыми, потому что они хранятся как 4-байтовые единицы в памяти (для ссылочных типов из-за своего адреса), что гарантирует операцию чтения / записи атомно. Тип double / long / etc не может быть энергозависимым, поскольку он не является атомарным для чтения / записи, так как в памяти содержится более 4 байтов. Правильно ли мое понимание?

И второе, если первое предположение верно, почему определяемая пользователем структура с одним полем int (или чем-то похожим, 4 байта в порядке) не может быть изменчивой? Теоретически это атомно, верно? Или это не допускается просто потому, что все определенные пользователем структуры (которые, возможно, превышают 4 байта) не допускаются к изменчивому по конструкции ?

Ответы [ 5 ]

1 голос
/ 14 января 2019

Итак, я полагаю, вы предлагаете добавить следующую точку:

  • Тип значения, состоящий только из одного поля, которое может быть юридически помечено как volatile.

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

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

Эта теоретическая и практическая причина означает, что единственнаяРеальным способом было бы ввести модификатор volatile для типов значений, который бы гарантировал выполнение указанной выше точки.Однако, поскольку единственная группа типов, которая извлечет выгоду из этого модификатора, - это типы значений с одним полем, эта функция, вероятно, была не слишком высока в списке.

1 голос
/ 12 ноября 2012

Обычно использование ключевого слова volatile может вводить в заблуждение.Его цель - разрешить, чтобы последнее значение (или фактически достаточно свежее значение) 1 соответствующего члена возвращалось при доступе из любого потока.

Фактически, это верно для типов значений только 2 .Члены ссылочного типа представлены в памяти как указатели на место в куче, где объект фактически хранится.Таким образом, при использовании ссылочного типа volatile гарантирует, что вы получите только новое значение ссылки (указателя) на объект, а не сам объект.

Если у вас есть volatile List<String> myVolatileList, который изменено несколькими потоками с добавлением или удалением элементов, и если вы ожидаете, что он безопасно получит доступ к последней модификации списка, вы на самом деле не правы .На самом деле вы подвержены тем же проблемам, что и если бы не было ключевого слова volatile - условий гонки и / или повреждения экземпляра объекта - это не поможет вам в этом случае и не обеспечит вам никакой безопасности потока.

Если, однако, сам список не изменен различными потоками, а, скорее, каждый поток будет только назначать другой экземпляр для поля (то есть список ведет себя как неизменныйобъект), тогда вы в порядке.Вот пример:

public class HasVolatileReferenceType
{
    public volatile List<int> MyVolatileMember;
}

Следующее использование корректно в отношении многопоточности, поскольку каждый поток заменяет указатель MyVolatileMember.Здесь volatile гарантирует, что другие потоки увидят последний экземпляр списка, сохраненный в поле MyVolatileMember.

HasVolatileReferenceTypeexample = new HasVolatileReferenceType();
// instead of modifying `example.MyVolatileMember`
// we are replacing it with a new list. This is OK with volatile.
example.MyVolatileMember = example.MyVolatileMember
     .Where(x => x > 42).ToList();

Напротив, приведенный ниже код подвержен ошибкам, поскольку он непосредственно изменяет список.Если этот код выполняется одновременно с несколькими потоками, список может быть поврежден или вести себя непоследовательно.

example.MyVolatileMember.RemoveAll(x => x <= 42);

Давайте ненадолго вернемся к типам значений.В .NET все типы значений на самом деле переназначаются , когда они изменяются, их можно безопасно использовать с ключевым словом volatile - см. Код:

public class HasVolatileValueType
{
    public volatile int MyVolatileMember;
}

// usage
HasVolatileValueType example = new HasVolatileValueType();
example.MyVolatileMember = 42;

1 Понятие позднего значения здесь немного вводит в заблуждение, как отмечает Эрик Липперт в разделе комментариев .Фактически, последнее здесь означает, что среда выполнения .NET будет пытаться (здесь нет никаких гарантий), чтобы предотвратить запись в энергозависимые элементы между операциями чтения всякий раз, когда она считает это возможным.Это способствовало бы тому, чтобы разные потоки считывали новое значение энергозависимого элемента, поскольку их операции чтения, вероятно, будут упорядочены после операции записи в элемент.Но на вероятность здесь можно рассчитывать больше.

2 Как правило, volatile можно использовать с любым неизменным объектом, поскольку изменения всегда подразумевают переназначение.поля с другим значением.Следующий код также является правильным примером использования ключевого слова volatile:

public class HasVolatileImmutableType
{
    public volatile string MyVolatileMember; 
}

// usage
HasVolatileImmutableType example = new HasVolatileImmutableType();
example.MyVolatileMember = "immutable";
// string is reference type, but is *immutable*, 
// so we need to reasign the modification result it in order 
// to work with the new value later
example.MyVolatileMember = example.MyVolatileMember.SubString(2);

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

0 голосов
/ 26 ноября 2012

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

KISS - Keep It Simple Simon - Это усложнит спецификацию и затруднит реализацию этой функции. Все языковые функции начинаются с минус 100 баллов , добавляет ли возможность иметь небольшую долю изменчивых стоек, действительно стоящих 101 балл?

Совместимость - вопросы сериализации в стороне - Обычно добавление нового поля к типу [class, struct] - это безопасный шаг, совместимый с обратным источником. Если вы добавляете поле, не должно нарушать компиляцию. Если поведение структур изменилось при добавлении поля, это сломало бы это.

0 голосов
/ 02 ноября 2012

Это обоснованное предположение при ответе ... пожалуйста, не подгоняйте меня слишком сильно, если я ошибаюсь!

Документация для энергозависимых состояний:

Модификатор volatile обычно используется для поля, к которому обращаются несколько потоков, без использования оператора блокировки для сериализации доступа.

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

Член структуры может обновляться независимо от других членов. Таким образом, чтобы записать новое значение структуры, в которой была изменена только его часть, необходимо прочитать старое значение. Таким образом, запись не требует единой операции с памятью. Это означает, что для надежного обновления структуры в многопоточной среде требуется некоторая блокировка или другая синхронизация потоков. Обновление нескольких элементов из нескольких потоков без синхронизации может вскоре привести к противоречивым, если не технически поврежденным результатам: сделать структуру volatile будет означать пометить неатомарный объект как атомарно обновляемый.

Кроме того, только некоторые структуры могут быть изменчивыми - размером 4 байта. Код, который определяет размер - определение структуры - может находиться в части программы, совершенно отличной от той, которая определяет поле как изменчивое. Это может сбить с толку, поскольку могут возникнуть непредвиденные последствия обновления определения структуры.

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

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

0 голосов
/ 25 февраля 2011

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

...