QNX/UNIX: Анатомия параллелизма
QNX/UNIX: Анатомия параллелизма читать книгу онлайн
Книга адресована программистам, работающим в самых разнообразных ОС UNIX. Авторы предлагают шире взглянуть на возможности параллельной организации вычислительного процесса в традиционном программировании. Особый акцент делается на потоках (threads), а именно на тех возможностях и сложностях, которые были привнесены в технику параллельных вычислений этой относительно новой парадигмой программирования. На примерах реальных кодов показываются приемы и преимущества параллельной организации вычислительного процесса. Некоторые из результатов испытаний тестовых примеров будут большим сюрпризом даже для самых бывалых программистов. Тем не менее излагаемые техники вполне доступны и начинающим программистам: для изучения материала требуется базовое знание языка программирования C/C++ и некоторое понимание «устройства» современных многозадачных ОС UNIX.
В качестве «испытательной площадки» для тестовых фрагментов выбрана ОСРВ QNX, что позволило с единой точки зрения взглянуть как на специфические механизмы микроядерной архитектуры QNX, так и на универсальные механизмы POSIX. В этом качестве книга может быть интересна и тем, кто не использует (и не планирует никогда использовать) ОС QNX: программистам в Linux, FreeBSD, NetBSD, Solaris и других традиционных ОС UNIX.
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
EINVAL
cond
int pthread_cond_broadcast(pthread_cond_t* cond);
Вызов функции разблокирует все потоки, блокированные на условной переменной
cond
Возвращаемые значения:
EOK
EFAULT
cond
mutex
EINVAL
cond
Разрушение условной переменной
int pthread_cond_destroy(pthread_cond_t* cond);
Вызов функции деинициализирует условную переменную
cond
cond
pthread_cond_init()
Возвращаемые значения:
EOK
EBUSY
cond
EINVAL
cond
Ждущая блокировка
QNX предоставляет упрощенный вариант использования условной переменной для блокирования (остановки) потока при помощи интерфейса так называемой ждущей блокировки( sleepon). Для использования этого механизма не нужно явно создавать никаких объектов синхронизации, за вас это делает ОС. Внешне ждущие блокировки выглядят как набор функций ожидания и освобождения, при этом последовательность действий в принципе аналогична использованию мьютексов и условных переменных.
За этим интерфейсом на самом деле скрывается один мьютекс и несколько дополнительных условных переменных. Использование функций ожидания должно проходить внутри участка кода, отмеченного вызовами блокирования и разблокирования мьютекса, ассоциированного со ждущей блокировкой. Одним из основных недостатков ждущей блокировки является то, что для всех потоков и всех ключей ожидания используется один общий мьютекс. ОС не может никоим образом отслеживать взаимные блокировки потоков при использовании ждущих блокировок. В целом поведение этого средства синхронизации идентично бинарным семафорам, но оно требует дополнительных операций блокирования мьютекса.
Все функции для работы со ждущими блокировками объявлены в файле
<pthread.h>
Операции со ждущей блокировкой
Захват и освобождение ждущей блокировки
Вызов функций ожидания может производиться только внутри блока захвата и освобождения ждущей блокировки:
int pthread_sleepon_lock(void);
int pthread_sleepon_unlock(void);
Функция захвата
pthread_sleepon_lock()
EOK
EDEADLK
EAGAIN
Функция освобождения
pthread_sleepon_unlock()
EOK
EPERM
Функции ожидания
Ожидание выполнения условия для ждущей блокировки может выполняться в двух вариантах: простое ожидание и ожидание с установкой тайм-аута.
int pthread_sleepon_wait(const volatile void* addr);
int pthread_sleepon_timedwait(const volatile void* addr, uint64_t nsec);
При вызове функций ожидания необходимо указать ключ
addr
Ожидание завершения потока
Ожидание родительским потоком завершения одного или нескольких порожденных им «присоединенных» потоков (на вызове
pthread_join()
void* threadfunc(void* data) {
...
return NULL;
}
...
// здесь создается нужное количество (N) потоков:
pthread_t tid[N];
for (int i = 0; i < N; i++)
pthread_create(tid + 1, NULL, threadfunc, NULL);
// а вот здесь ожидается завершение <b>всех</b>потоков!
for (int i = 0; i < N; i++)
pthread_join(tid + 1, NULL);
При первом знакомстве с подобным шаблоном кода пугает то обстоятельство, что предписан такой же порядок ожидания завершения потоков, как и при их создании. И это при том, что порядок их завершения может быть совершенно произвольным. Но представленный шаблон верен: если некоторый ожидаемый в текущем цикле поток
j
tid[j]
i
tid[i]
В такой схеме потоки могут возвратить в точку ожидания (и зачастую делают это) результат своего выполнения. В представленном шаблоне мы не стали показывать возврат значений, чтобы не загромождать код. Возврат результата подробно рассматривался ранее, когда речь шла о завершении потоков.