Чтение взаимно блокируемых переменных

JSF 2.3 +

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


    #{notification.message}



    
        
    

@Named @ApplicationScoped
public class Bean {

    private List notifications;

    @Inject
    private NotificationService service;

    @Inject @Push
    private PushContext push;

    @PostConstruct
    public void load() {
        notifications = service.list();
    }

    public void onNewNotification(@Observes Notification newNotification) {
        notifications.add(0, newNotification);
        push.send("updateNotifications");
    }

    public List getNotifications() {
        return notifications;
    }

}

@Stateless
public class NotificationService {

    @Inject
    private EntityManager entityManager;

    @Inject
    private BeanManager beanManager;

    public void create(String message) {
        Notification newNotification = new Notification();
        newNotification.setMessage(message);
        entityManager.persist(newNotification);
        beanManager.fireEvent(newNotification);
    }

    public List list() {
        return entityManager
            .createNamedQuery("Notification.list", Notification.class)
            .getResultList();
    }

}

JSF 2.2 -

Если вы еще не находитесь в JSF 2.3, вам нужно перейти в сторонние библиотеки JSF.

Отмечено, что является основой для JSF 2.3 ]. Итак, если вы нашли много общего, то это правильно.

PrimeFaces использует Атмосфера под капюшонами (что неудобно для настройки без Maven). Атмосфера поддерживает websockets с возвратом SSE и длительным опросом. ICEfaces основан на древней методике длинного опроса . Все они не реализуют собственный API-интерфейс JSR356 WebSocket, который был позже представлен в Java EE 7.

OmniFaces использует собственный API-интерфейс JSR356 WebSocket (поддерживается на всех серверах Java EE 7 и Tomcat 7.0.27+). Поэтому он также очень прост в настройке и использовании (один JAR, один параметр контекста, один тег и одна аннотация). Для этого требуется только CDI (не сложно установить на Tomcat ), но он позволяет вам даже нажать от артефакта не-JSF (например, @WebServlet). В целях безопасности и безопасности JSF, он поддерживает только односторонний push (от сервера к клиенту), а не наоборот. Для этого вы можете использовать JSF ajax обычным способом. JSF 2.3 в значительной степени основан на OmniFaces , поэтому вы найдете много сходства в своих API-интерфейсах ( JSF - OmniFaces ).

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

См. Также:

25
задан Marc Mutz - mmutz 27 July 2009 в 05:13
поделиться

8 ответов

Любому, кто должен вернуться к этой теме, я хочу добавить к тому, что было хорошо объяснено Бартошем, что _InterlockedCompareExchange() является хорошей альтернативой стандартному atomic_load(), если стандартная атомика недоступна. Вот код для атомарного чтения my_uint32_t_var в C на i86 Win64. atomic_load() включено в качестве эталона:

 long debug_x64_i = std::atomic_load((const std::_Atomic_long *)&my_uint32_t_var);
00000001401A6955  mov         eax,dword ptr [rbp+30h] 
00000001401A6958  xor         edi,edi 
00000001401A695A  mov         dword ptr [rbp-0Ch],eax 
    debug_x64_i = _InterlockedCompareExchange((long*)&my_uint32_t_var, 0, 0);
00000001401A695D  xor         eax,eax 
00000001401A695F  lock cmpxchg dword ptr [rbp+30h],edi 
00000001401A6964  mov         dword ptr [rbp-0Ch],eax 
    debug_x64_i = _InterlockedOr((long*)&my_uint32_t_var, 0);
00000001401A6967  prefetchw   [rbp+30h] 
00000001401A696B  mov         eax,dword ptr [rbp+30h] 
00000001401A696E  xchg        ax,ax 
00000001401A6970  mov         ecx,eax 
00000001401A6972  lock cmpxchg dword ptr [rbp+30h],ecx 
00000001401A6977  jne         foo+30h (01401A6970h) 
00000001401A6979  mov         dword ptr [rbp-0Ch],eax 

    long release_x64_i = std::atomic_load((const std::_Atomic_long *)&my_uint32_t_var);
00000001401A6955  mov         eax,dword ptr [rbp+30h] 
    release_x64_i = _InterlockedCompareExchange((long*)&my_uint32_t_var, 0, 0);
00000001401A6958  mov         dword ptr [rbp-0Ch],eax 
00000001401A695B  xor         edi,edi 
00000001401A695D  mov         eax,dword ptr [rbp-0Ch] 
00000001401A6960  xor         eax,eax 
00000001401A6962  lock cmpxchg dword ptr [rbp+30h],edi 
00000001401A6967  mov         dword ptr [rbp-0Ch],eax 
    release_x64_i = _InterlockedOr((long*)&my_uint32_t_var, 0);
00000001401A696A  prefetchw   [rbp+30h] 
00000001401A696E  mov         eax,dword ptr [rbp+30h] 
00000001401A6971  mov         ecx,eax 
00000001401A6973  lock cmpxchg dword ptr [rbp+30h],ecx 
00000001401A6978  jne         foo+31h (01401A6971h) 
00000001401A697A  mov         dword ptr [rbp-0Ch],eax
1
ответ дан Sergey D 28 November 2019 в 21:46
поделиться

Это зависит от того, что вы подразумеваете под «целью является просто прочитать текущее значение _ServerState», и это зависит от того, какой набор инструментов и платформу вы используете (вы указываете Win32 и C ++, но не какой компилятор C ++, и что может иметь значение).

Если вы просто хотите прочитать значение таким образом, чтобы оно не было повреждено (т. Е. Если какой-либо другой процессор меняет значение с 0x12345678 на 0x87654321, то ваше чтение получит одно из этих 2 значений, а не 0x12344321), тогда простое чтение будет все будет в порядке, пока переменная:

  • помечена volatile,
  • правильно выровнена и
  • прочитана с использованием одной инструкции с размером слова, который процессор обрабатывает атомарно

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

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

struct mailbox_struct {
    uint32_t flag;
    uint32_t data;
};
typedef struct mailbox_struct volatile mailbox;


// the global - initialized before wither thread starts

mailbox mbox = { 0, 0 };

//***************************
// Thread A

while (mbox.flag == 0) { 
    /* spin... */ 
}

uint32_t data = mbox.data;

//***************************

//***************************
// Thread B

mbox.data = some_very_important_value;
mbox.flag = 1;

//***************************

Мысль о том, что поток А будет вращаться в ожидании mbox.flag, чтобы указать, что mbox.data содержит действительный фрагмент информации. Поток B запишет некоторые данные в mailbox.data, а затем установит для mbox.flag значение 1 в качестве сигнала о том, что mbox.data действителен.

В этом случае простое чтение в потоке A mbox.flag может получить значение 1, даже если последующее чтение mbox.data в потоке A не получит значение, записанное потоком B.

Это связано с тем, что, хотя компилятор не будет переупорядочивать запись потока B в mbox.data и mbox.flag, процессор и / или кэш могут. C / C ++ гарантирует, что компилятор сгенерирует код таким образом, что поток B запишет в mbox.data до того, как он запишет в mbox.flag, но процессор и кэш могут иметь другое представление - специальная обработка, называемая «барьеры памяти» или «приобретение и семантика релиза 'должна использоваться для обеспечения порядка ниже уровня потока инструкций потока.

Я не уверен, что компиляторы, кроме MSVC, утверждают, что упорядочены ниже уровня инструкции. Однако MS гарантирует, что для MSVC достаточно volatile - MS указывает, что volatile записи имеют семантику выпуска, а volatile чтения приобретают семантику - хотя я не уверен, к какой версии MSVC это применимо - см. http: // msdn. microsoft.com/en-us/library/12a04hfd.aspx?ppud=4.

Я также видел подобный вам код, который использует Interlocked APIs для выполнения простых операций чтения и записи в общие местоположения. Мое мнение по этому вопросу заключается в использовании взаимосвязанных API. Блокировка без связи между потоками полна очень трудных для понимания и тонких ловушек, и попытка сократить критический фрагмент кода, который может привести к очень сложной диагностике ошибки, не кажется мне хорошей идеей , Кроме того, используя Interlocked API, выкрикивает всем, кто поддерживает код: «Это доступ к данным, который должен использоваться совместно или синхронизироваться с чем-то другим - действуйте осторожно! ».

Кроме того, при использовании Interlocked API вы берете на себя специфику аппаратного обеспечения и компилятора - платформа следит за тем, чтобы все эти вещи обрабатывались должным образом - больше не удивительно ...

Прочитайте статьи Херба Саттера «Эффективный параллелизм» о DDJ (которые сейчас не работают, по крайней мере для меня) для получения хорошей информации по этой теме.

13
ответ дан Michael Burr 28 November 2019 в 21:46
поделиться

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

0
ответ дан Alphaneo 28 November 2019 в 21:46
поделиться

Ваше первоначальное понимание в основном верно. В соответствии с моделью памяти, которая требуется Windows на всех платформах MP, которые она поддерживает (или когда-либо будет поддерживать), чтения из естественно выровненной переменной, помеченной как volatile, являются атомарными, если они меньше, чем размер машинного слова. То же самое с пишет. Вам не нужен префикс «lock».

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

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

long g_var = 0;  // not marked 'volatile' -- this is an error

bool foo () {
    long oldValue;
    long newValue;
    long retValue;

    // (1) Capture the original global value
    oldValue = g_var;

    // (2) Compute a new value based on the old value
    newValue = SomeTransformation(oldValue);

    // (3) Store the new value if the global value is equal to old?
    retValue = InterlockedCompareExchange(&g_var,
                                          newValue,
                                          oldValue);

    if (retValue == oldValue) {
        return true;
    }

    return false;
}

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

Таким образом, шаг (3) функции станет:

// (3) Incorrectly store new value regardless of whether the global
//     is equal to old.
retValue = InterlockedCompareExchange(&g_var,
                                      newValue,
                                      g_var);
0
ответ дан Neeraj Singh 28 November 2019 в 21:46
поделиться

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

5
ответ дан 28 November 2019 в 21:46
поделиться

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

С другой стороны, какова дополнительная стоимость использования защищенных подпрограмм?

0
ответ дан 28 November 2019 в 21:46
поделиться

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

0
ответ дан 28 November 2019 в 21:46
поделиться

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

Если вам нужен какой-то флаг, вам следует рассмотреть возможность использования для этой цели объекта Event и функции WaitForSingleObject .

1
ответ дан 28 November 2019 в 21:46
поделиться