неизбежной «гонки» между истечением периода ожидания и изменением состояния предиката.

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

В общем случае при каждом завершении ожидания по условию поток должен оценивать значение предиката, связанного с ожиданием по условию, чтобы узнать, может ли он безопасно продолжать выполнение, ожидать или объявить тайм-аут. Возврат из состояния ожидания не означает, что соответствующий предикат имеет конкретное значение (ЛОЖЬ или ИСТИНА).

Поэтому рекомендуется ожидание по условию выражать в коде, эквивалентно м циклу «while», который выполняет проверку предиката.

Семантика ожидания по времени

Абсолютное время было выбрано для задания параметра лимита времени по двум причинам. Во-первых, несмотря на то, что измерение относительного времени нетрудно реализовать в начале функции, для которой задается абсолютное время, с заданием абсолютного времени в начале функции, которая определяет относительное время, связано условие «гонок». Предположим, например, что функция clock_gettime() возвращает текущее время, а функция cond_relative_timed_wait () использует относительное время.

clock_gettime(CLOCK_REALTIME, &now)

reltime = sleep_til_this_absolute_time -now;

cond_relative_timed_wait (с, m, &reltime);

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

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

Аннулирование потока и ожидание по условию

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

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

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

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

Быстродействие мьютексов и условных переменных

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

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

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

Особенности мьютексов и условных переменных

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

Планирование поведения мьютексов и условных переменных

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

Синхронизированное ожидание по условию

Функция pthread_cond_timedwait () позволяет приложению прервать ожидание наступления конкретного условия после истечения заданного интервала времени. Рассмотрим следующий пример.

(void) pthread_mutex_lock (&t. mn);

t.waiters++;

clock_gettime (CLOCK_REALTIME, &ts) ;

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату