Безопасна ли установка копий из неизменяемых файлов от повреждения потока?

Вот поле класса,

class someClass {
Int someClassField = nil
...

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

class someClass {
List<List> someClassField = new List<Int>()
...

Как я понимаю, если поле представляет собой простой список, один поток может оставить его в несогласованном состоянии (т.е. частично отключен). Если другой поток использует список, он потерпит неудачу — на языке, подобном C, это будет катастрофой. Даже чтение может потерпеть неудачу.

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

Я игнорирую все проблемы, связанные с тем, может ли языковой движок переупорядочивать, кэшировать и т. Д. См. Много постов ниже (особенно Java, кажется),

Я хотел бы проработать этот вопрос в меньшем масштабе …

0

Решение

В большинстве языков присвоение объекта является атомарным.

В этом конкретном случае вы должны быть осторожны, так как x=new X() на всех языках нет гарантии, что X полностью инициализирован перед присваиванием. Я не уверен, где C # стоит на этом.

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

1

Другие решения

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

1

Запись ссылки на Java является атомарной (запись в long или double выполняется только в том случае, если поле изменчиво между прочим), но это само по себе совсем не помогает.

Пример для демонстрации:

class Foo {
int x;
public Foo() { x = 5};
}

Теперь предположим, что мы делаем назначение, такое как foo = new Foo() (без окончательных или изменчивых модификаторов для foo!). С точки зрения низкого уровня, это означает, что мы должны сделать следующее:

  1. выделить память
  2. запустить конструктор
  3. назначить адрес памяти для поля.

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

  1. выделить память
  2. назначить адрес памяти для поля.
  3. запустить конструктор

Резьбонарезной безопасно? Конечно, нет (и вы никогда не увидите обновления, если не вставите барьеры памяти). Java дает больше гарантий, когда используются конечные поля, поэтому создание нового неизменяемого объекта будет поточно-ориентированным (вы никогда не увидите неинициализированное значение конечного поля). Изменчивые поля (здесь речь идет о присваивании, а не полях в объекте), также позволяют избежать этой проблемы как в Java, так и в C #. Не уверен насчет C # и только для чтения.

1

В Java присвоение ссылкам и примитивам является атомарным, за исключением 64-битных типов примитивов. long а также double, Назначения на Java longс и doubleможно сделать атомарными, объявив их с volatile модификатор. Увидеть: Являются ли 64-битные назначения в Java атомарными на 32-битной машине?

Это так, потому что спецификация Java VM требует этого для того, чтобы VM была Java-совместимой.

Scala работает поверх стандартной виртуальной машины Java и поэтому предоставляет те же гарантии, что и Java, в отношении назначений, если только они не начнут использовать JNI.

Одна из проблем с C / C ++ (и одна из его сильных сторон) заключается в том, что оба языка позволяют очень тонко отображать структуры данных на адреса памяти. На этом уровне то, являются ли записи в память атомарными или нет, сильно зависит от аппаратной платформы. Например, процессоры обычно не могут атомарно считывать, не говоря уже о записи в переменные, которые не выровнены соответствующим образом. например Когда 16-битные переменные не выровнены по четным адресам, или когда 32-битные переменные не выровнены по адресам, кратным 4, и так далее. Ситуация ухудшается, когда переменная выходит за пределы одной строки кэша в следующую или за одну страницу в следующую. Следовательно, C не гарантирует, что назначения будут атомарными.

1
По вопросам рекламы [email protected]