Выбрать главу

Предусмотрительно ограничивая использование понятия «неопределенное поведение» только случаями ошибочных действий самого приложения (по причине недостаточно продуманного кода) и обязательно определяя ошибки, связанные с недоступностью системных ресурсов, данный том стандарта IEEE Std 1003.1-2001 гарантирует, что любое корректно написанное приложение переносимо в полном диапазоне реализаций, но не обязывает все реализации нести дополнительные затраты на проверку многочисленных условий, которые корректно написанная программа никогда не создаст.

Почему не определяются предельные значения

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

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

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

Без применения статической инициализации функция самоинициализации foo () может иметь следующий вид.

static pthread_once_t foo_once = PTHREAD_ONCE_INIT;

static pthread_mutex_t foo_mutex;

void foo_init () {

pthread_mutex_init (&foo_mutex, NULL);

}

void foo() {

pthread_once(&foo_once, foo_init);

pthread_mutex_lock (&foo_mutex);

/* Выполнение действий. */

pthread_mutex_unlock (&foo_mutex);

}

С применением статической инициализации ту же функцию самоинициализации foo() м ожно было бы закодировать таки м образо м.

static pthread_mutex_t foo_mutex = PTHREAD_MUTEX_INITIALIZER;

void foo()

{

pthread_mutex_lock(&foo_mutex) ;

/* Выполнение действий. */

pthread_mutex_unlock(&foo_mutex);

}

Обратите внимание на то, что статическая инициализация устраняет необходимость в тестировании, проводимом в функции pthread_once (), и получении значения адреса &foo_mutex, передаваемого функции pthread_mutex_lock() или pthread_mutex_unlock ().

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

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

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