int pthread_rwlock_tryrdlock(pthread_rwlock_t *
int pthread_rwlock_wrlock(pthread_rwlock_t*
int pthread_rwlock_trywrlock(pthread_rwlock_t *
Существует четыре функции блокировки, а не две, как вы могли бы предположить. Очевидно, «предполагаемыми» функциями были
Это — собственно блокирующие вызовы: если блокировка для выбранной операции недоступна, поток будет блокирован. Когда блокировка становится доступной в соответствующем режиме, поток будет разблокирован, из чего он сможет предположить, что теперь можно спокойно обращаться к защищенному блокировкой ресурсу.
Иногда, тем не менее, поток может не захотеть блокироваться, желая вместо этого просто узнать, доступна ли нужная блокировка. Для этого и существуют версии функций, содержащие в имени «try» («проверка»). Важно отметить, что «проверочные» версии получат блокировку, если она доступна, но если нет, тогда они не будут блокированы, а только возвратят код ошибки. Причина, по которой они должны получать блокировку, если она доступна, очень проста. Предположим, что поток хочет получить блокировку на чтение, но не хочет ждать, если блокировка окажется недоступной. Поток вызывает функцию
Наконец, независимо от того, как блокировка нами применялась, нам необходим способ ее освобождения:
int pthread_rwlock_unlock(pthread_rwlock_t*
После того как поток выполнил нужную операцию с ресурсом, он освобождает блокировку, вызывая функцию
Отметим, что мы не смогли бы реализовать такую форму синхронизации только с помощью мутекса. Мутекс рассчитан только на один поток, что было бы хорошо в случае записи (чтобы только один поток мог использовать ресурс в определенный момент времени), но оплошал бы в случае считывания, потому что не допустил бы к ресурсу более чем одного «читателя». Семафор также был бы бесполезен, потому что нельзя было бы отличить два режима доступа — применение семафора могло бы обеспечить доступ нескольких «читателей», но если бы семафором попытался завладеть «писатель», его вызов ничем бы не отличался от вызова «читателей», что вызвало бы некрасивую ситуацию с множеством «читателей» и множеством же «писателей»!
Ждущие блокировки
Другая типовая ситуация в многопоточных программах — это потребность заставить поток «ждать чего-либо». Этим «чем- либо» может являться фактически что угодно! Например, когда доступны данные от устройства, или когда конвейерная лента находится в нужной позиции, или когда данные сохранены на диск, и т.д. Еще одна хитрость этой ситуации состоит в том, что одного и того же события могут ожидать несколько потоков.
Для таких целей мы могли бы использовать либо условную переменную (condition variable), о которой речь ниже, либо, что гораздо проще, ждущую блокировку (sleepon).
Для применения ждущих блокировок надо выполнить несколько операций. Рассмотрим сначала вызовы, а затем вернемся к использованию ждущих блокировок.
int pthread_sleepon_lock(void);
int pthread_sleepon_unlock(void);
int pthread_sleepon_broadcast(void *addr);
int pthread_sleepon_signal(void *addr);
int pthread_sleepon_wait(void *addr);