Я продолжаю бороться, чтобы понять, что VarHandle::setOpaque и VarHandle::getOpaque делают на самом деле . До сих пор это было нелегко - есть некоторые вещи, которые я думаю получаю (но не буду представлять их в самом вопросе, а не в мутной воде), но в целом это в лучшем случае мисс лидирует для меня.

Документация:

Возвращает значение переменной, доступ к которой осуществляется в программном порядке ...

Ну, в моем понимании, если у меня есть:

int xx = x; // read x
int yy = y; // read y

Эти чтения могут быть переупорядочены. С другой стороны, если у меня есть:

// simplified code, does not compile, but reads happen on the same "this" for example
int xx = VarHandle_X.getOpaque(x); 
int yy = VarHandle_Y.getOpaque(y);

На этот раз повторные заказы невозможны? И это что значит "программный заказ"? Мы говорим о вставке барьеров здесь для того, чтобы этот повторный заказ был запрещен? Если это так, поскольку это две нагрузки, будет ли достигнуто то же самое? через:

 int xx = x;
 VarHandle.loadLoadFence()
 int yy = y;

Но это становится намного сложнее:

... но без гарантии эффектов упорядочения памяти по отношению к другим потокам.

Я не мог придумать пример, чтобы притвориться, что понимаю эту часть.

Мне кажется, что эта документация предназначена для людей, которые точно знают, что они делают (а я определенно не один) ... Так может кто-то пролить свет на это?

7
Eugene 28 Май 2019 в 15:19

2 ответа

Лучший ответ

Ну, в моем понимании, если у меня есть:

int xx = x; // read x
int yy = y; // read y

Эти чтения могут быть переупорядочены.

Эти чтения могут не только быть переупорядочены, они могут не произойти вообще. Поток может использовать старое, ранее прочитанное значение для x и / или y или значений, которые он ранее записывал в эти переменные, тогда как на самом деле запись, возможно, еще не была выполнена, поэтому " «Чтение потока» может использовать значения, о которых другие потоки не могут знать и не находятся в памяти кучи в это время (и, вероятно, никогда не будут).

С другой стороны, если у меня есть:

// simplified code, does not compile, but reads happen on the same "this" for example
int xx = VarHandle_X.getOpaque(x); 
int yy = VarHandle_Y.getOpaque(y);

На этот раз повторные заказы невозможны? И это что значит "программный заказ"?

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

Термин порядок программ определяется JLS:

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

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

Мы говорим о вставке барьеров здесь для того, чтобы этот повторный заказ был запрещен?

Нет, в этом нет никакого препятствия, которое может заключаться в фразе «… но без гарантии эффектов упорядочения памяти по отношению к другим потокам ».

Возможно, мы могли бы сказать, что непрозрачный доступ работает так же, как volatile до Java 5, обеспечивая доступ для чтения, чтобы увидеть самое последнее значение памяти кучи (что имеет смысл, только если конец записи также использует непрозрачный или даже более сильный режим). ), но без влияния на другие операции чтения или записи.

Так что вы можете сделать с этим?

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

Аналогичным образом, фоновая задача может записывать обновления прогресса, например, процентное число, которое поток отчетов (UI) должен своевременно замечать, в то время как перед публикацией окончательного результата не требуется никаких отношений произойдет до / <>. ,

Это также полезно, если вы просто хотите атомарный доступ для long и double, без какого-либо другого влияния.

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

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

8
Holger 28 Май 2019 в 14:46

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

Другие темы могут наблюдать за действиями потоков в любом порядке. На x86 это частый случай, так как

упорядочить запись с пересылкой из буфера

Модель памяти, так что даже если поток сохраняет до загрузки. Хранилище может быть кэшировано в буфере хранилища, и некоторый поток, выполняемый на любом другом ядре, наблюдает за действием потока в обратном порядке загрузки-хранения вместо сохранения загрузки. Таким образом, непрозрачная операция выполняется на x86 бесплатно (на x86 мы на самом деле также получили бесплатно, смотрите этот исчерпывающий ответ для деталей о некоторых других архитектурах и их моделях памяти: https://stackoverflow.com/a/55741922/8990329 )

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

Кроме того, поскольку Java 9 VarHandles в некоторой степени связаны с семантикой получения / выпуска / использования в C, я думаю, что стоит отметить, что непрозрачный доступ похож на memory_order_relaxed, который определен в Стандарте следующим образом:

Для memory_order_relaxed ни одна операция не заказывает память.

С некоторыми примерами.

0
Some Name 5 Июн 2019 в 02:40
56342141