AtomicInteger.lazyset () - задержка видимости [дубликат]

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

Начните с наивного подхода (который не работает) для функция, вызывающая асинхронный метод (в данном случае setTimeout) и возвращает сообщение:

function getMessage() {
  var outerScopeVar;
  setTimeout(function() {
    outerScopeVar = 'Hello asynchronous world!';
  }, 0);
  return outerScopeVar;
}
console.log(getMessage());

undefined регистрируется в этом случае, поскольку getMessage возвращается до вызова setTimeout и обновления outerScopeVar.

Двумя основными способами его решения являются обратные вызовы и обещания:

Обратные вызовы

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

function getMessage(callback) {
  setTimeout(function() {
    callback('Hello asynchronous world!');
  }, 0);
}
getMessage(function(message) {
  console.log(message);
});

Обещает

Обещания предоставляют альтернативу который является более гибким, чем обратные вызовы, потому что их можно комбинировать естественно для координации нескольких асинхронных операций. Стандартная реализация Promises / A + изначально представлена ​​в node.js (0.12+) и многих текущих браузерах, но также реализована в таких библиотеках, как Bluebird и Q .

function getMessage() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Hello asynchronous world!');
    }, 0);
  });
}

getMessage().then(function(message) {
  console.log(message);  
});

jQuery Отложенные

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

function getMessage() {
  var deferred = $.Deferred();
  setTimeout(function() {
    deferred.resolve('Hello asynchronous world!');
  }, 0);
  return deferred.promise();
}

getMessage().done(function(message) {
  console.log(message);  
});

async / await

Если ваша среда JavaScript включает поддержку async и await (например, Node.js 7.6+), то вы может использовать обещания синхронно в пределах функций async:

function getMessage () {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve('Hello asynchronous world!');
        }, 0);
    });
}

async function main() {
    let message = await getMessage();
    console.log(message);
}

main();

94
задан David Harkness 3 September 2014 в 18:34
поделиться

6 ответов

Цитируется прямо из Bug 6275329 :

Как, вероятно, последнее небольшое наблюдение за JSR166 для Mustang, мы добавили метод «lazySet» для классов Atomic ( AtomicInteger, AtomicReference и т. Д.). Это метод ниши, который иногда полезен при тонкой настройке кода с использованием неблокирующих структур данных. Семантика заключается в том, что запись гарантируется, что она не должна быть переупорядочена с любой предыдущей записью, но может быть переупорядочена с последующими операциями (или, что то же самое, может не отображаться для других потоков) до тех пор, пока не произойдет какое-то другое летучее действие записи или синхронизации).

Основным вариантом использования является обнуление полей узлов в неблокирующих структурах данных исключительно для того, чтобы избежать долгосрочного удержания мусора; это применимо, когда это безопасно, если другие потоки видят ненулевые значения некоторое время, но вы хотите, чтобы структуры были в конечном итоге GCable. В таких случаях вы можете получить более высокую производительность, избегая затрат на нулевую volatile-write. Существует несколько других вариантов использования этих строк для атомарности без ссылки, поэтому метод поддерживается во всех классах AtomicX.

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

100
ответ дан yawn 3 September 2018 в 09:30
поделиться

lazySet может использоваться для связи между потоками rmw, потому что xchg является атомарным, так как для видимости, когда процесс потока писем изменяет местоположение строки кэша, процессор считывающего потока увидит его при следующем чтении, потому что протокол согласованности кеширования intel cpu будет гарантировать, что LazySet работает, но строка кэша будет обновлена ​​при следующем чтении, опять же, процессор должен быть достаточно современным.

http://sc.tamu.edu/systems/ eos / nehalem.pdf Для Nehalem, которая является многопроцессорной платформой, процессоры имеют возможность «отслеживать» (подслушивать) адресную шину для доступа другого процессора к системной памяти и к их внутренним кэшам. Они используют эту способность отслеживания, чтобы сохранить свои внутренние тайники как в системной памяти, так и в кэшах в других взаимосвязанных процессорах. Если через отслеживание одного процессора обнаруживается, что другой процессор намеревается записать в ячейку памяти, которую он в настоящее время кэшировал в общем состоянии, процессор отслеживания сделает недействительным свой блок кэша, заставляя его выполнять кеш-строку, заполняя при следующем обращении к той же ячейке памяти .

oracle hotspot jdk для архитектуры x86 cpu ->

lazySet == unsafe.putOrderedLong == xchg rw (инструкция asm, которая служит в качестве мягкого барьера стоимостью 20 циклов на процессоре nehelem intel cpu )

на x86 (x86_64), такой барьер намного дешевле, чем волатильный или AtomicLong getAndAdd,

В одном продюсере один сценарий очереди потребителей, мягкий барьер xchg может заставить строка кодов перед тем, как lazySet (последовательность + 1) для потока производителя произойдет до любого кода потребительского потока, который будет потреблять (работать) с новыми данными, конечно, потребительский поток должен будет проверить атомарно, что последовательность производителей была увеличена ровно на один используя compareAndSet (последовательность, последовательность + 1).

Я проследил после исходного кода Hotspot, чтобы найти точное сопоставление lazySet с кодом cpp: http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/ src / share / vm / prims / unsafe.cpp Unsafe_setOrderedLong -> SET_FIELD_VOLATILE definition -> OrderAccess: release_store_fence. Для x86_64 OrderAccess: release_store_fence определяется как использование инструкции xchg.

Вы можете видеть, как это точно определено в jdk7 (doug lea работает над некоторыми новыми материалами для JDK 8): http://hg.openjdk.java.net/jdk7/jdk7/ hotspot / file / 4fc084dac61e / src / os_cpu / linux_x86 / vm / orderAccess_linux_x86.inline.hpp

вы также можете использовать hdis для демонстрации сборки кода lazySet в действии.

Есть еще один связанный с этим вопрос: Нужно ли нам использовать mfence при использовании xchg

13
ответ дан Community 3 September 2018 в 09:30
поделиться

Более широкое обсуждение истоков и полезности lazySet и лежащего в основе putOrdered можно найти здесь: http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance -win-for.html

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

При чтении значения, которое вы всегда делаете volatile read (с Atomic * .get () в любом случае).

lazySet предлагает единому писателю согласованный волатильный механизм записи, то есть совершенно законно для одного писателя использовать lazySet для увеличения счетчик, несколько потоков, увеличивающих один и тот же счетчик, должны будут разрешить конкурирующие записи с использованием CAS, что и происходит под обложками Atomic * для incAndGet.

8
ответ дан Nitsan Wakart 3 September 2018 в 09:30
поделиться

Re: попытка отключить его -

Вы можете думать об этом как о способе лечения изменчивого поля, как если бы он не был изменен для определенного хранилища (например: ref = null;)

Это не совсем точно, но этого должно быть достаточно, чтобы вы могли принять решение между «ОК, мне действительно все равно» и «Хм, позвольте мне немного подумать об этом ».

2
ответ дан Paul Mclachlan 3 September 2018 в 09:30
поделиться

Из сводки с одновременным атомным пакетом

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

Если вам интересно узнать о lazySet, то вы также должны себе другие объяснения

Эффекты памяти для доступа и обновлений атомистики обычно следуют правилам для летучих, как указано в разделе 17.4 Спецификации языка Java ™.

get имеет эффекты памяти при чтении изменчивой переменной.

set имеет эффекты памяти записи (назначения) изменчивой переменной.

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

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

6
ответ дан Victor Stafusa 3 September 2018 в 09:30
поделиться

Вот мое понимание, исправьте меня, если я ошибаюсь: вы можете думать о lazySet() как «полу» volatile: это, в основном, энергонезависимая переменная с точки зрения чтения другими потоками, то есть значение, заданное lazySet, может не быть видимым для других потоков. Но он становится изменчивым, когда происходит другая операция записи (может быть из других потоков). Единственное влияние lazySet, которое я могу себе представить, - compareAndSet. Поэтому, если вы используете lazySet(), get() из других потоков могут все еще получать старое значение, но compareAndSet() всегда будет иметь новое значение, так как это операция записи.

3
ответ дан Vladimir Vagaytsev 3 September 2018 в 09:30
поделиться
Другие вопросы по тегам:

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