Подразумевает ли вызов pthread_cond_signal или pthread_cond_broadcast барьер записи в память?

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

flag = 1;
pthread_cond_broadcast(&cvar);

Однако это безопасно только в том случае, если pthread_cond_broadcast подразумевает барьер записи в память; в противном случае ожидающий поток может увидеть широковещательную передачу условной переменной до записи флага. То есть ожидающий поток может пробудиться, использовать сигнал cvar, но все еще видеть флаг 0.

Итак, мой вопрос: подразумевают ли вызовы pthread_cond_broadcast и pthread_cond_signal барьер записи в память? Если да, то где это указано в соответствующих спецификациях POSIX (или других)? Спецификация показалась неясной в этом вопросе.

Примечание. Я знаю, что на практике это приводит к барьеру памяти (в Linux, потому что пробуждение потока подразумевает полный барьер памяти ЦП, а вызов функции между библиотеками подразумевает барьер памяти компилятора). Однако меня интересует, что гарантирует спецификация.


person bdonlan    schedule 29.08.2011    source источник


Ответы (3)


Независимо от того, подразумевает ли это барьер памяти, код все равно неверен. Рассмотрим сторону чтения:

while (flag == 0)
    pthread_cond_wait(&cvar, &mutex);

Если сторона чтения приостановлена ​​между проверкой flag == 0 и выполнением ожидания, сторона записи может выполнить flag = 1; pthread_cond_signal(&cvar);. Затем сторона чтения полностью пропустит пробуждение - она ​​будет ждать вечно. Помните, что пробуждения не ставятся в очередь — если нет ожидания, когда сигнализируется условная переменная, сигнал не имеет никакого эффекта. Чтобы избежать этого, сторона записи все равно должна заблокировать мьютекс.

person caf    schedule 30.08.2011
comment
Технически POSIX обещает, что pthread_cond_broadcast() является барьером памяти (pubs.opengroup.org /onlinepubs/9699919799/basedefs/). Но, поскольку ваш ответ хорошо детализирован, это бесполезно для поставленной цели. - person Michael Burr; 31.08.2011

В POSIX, если вы пишете в переменную из одного потока и читаете ее из другого, вы должны защитить ее с помощью мьютекса. Для pthread_cond_broadcast не делается никаких исключений.

Если ваша платформа/компилятор предлагает атомарные переменные, они могут дать дополнительные гарантии относительно них. Например, если flag является C++11 std::atomic<int>, то этот код подходит.

person Anthony Williams    schedule 30.08.2011

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

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

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

Так что: не делай этого.

person curiousguy    schedule 15.08.2020