QNX/UNIX: Анатомия параллелизма
QNX/UNIX: Анатомия параллелизма читать книгу онлайн
Книга адресована программистам, работающим в самых разнообразных ОС UNIX. Авторы предлагают шире взглянуть на возможности параллельной организации вычислительного процесса в традиционном программировании. Особый акцент делается на потоках (threads), а именно на тех возможностях и сложностях, которые были привнесены в технику параллельных вычислений этой относительно новой парадигмой программирования. На примерах реальных кодов показываются приемы и преимущества параллельной организации вычислительного процесса. Некоторые из результатов испытаний тестовых примеров будут большим сюрпризом даже для самых бывалых программистов. Тем не менее излагаемые техники вполне доступны и начинающим программистам: для изучения материала требуется базовое знание языка программирования C/C++ и некоторое понимание «устройства» современных многозадачных ОС UNIX.
В качестве «испытательной площадки» для тестовых фрагментов выбрана ОСРВ QNX, что позволило с единой точки зрения взглянуть как на специфические механизмы микроядерной архитектуры QNX, так и на универсальные механизмы POSIX. В этом качестве книга может быть интересна и тем, кто не использует (и не планирует никогда использовать) ОС QNX: программистам в Linux, FreeBSD, NetBSD, Solaris и других традиционных ОС UNIX.
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
for (int i = 0; i < N; i++) {
pthread_attr_init(pattr);
// ... разнообразные настройки для разных потоков ...
pthread_create(NULL, pattr, &function, NULL);
pthread_attr_destroy(pattr);
}
delete pattr;Непосредственно манипулировать с полями атрибутной записи, адресуясь к ним по именам полей, крайне опасно. Для этого предусмотрен широкий спектр функций SET/GET:
pthread_attr_getdetachstate()
pthread_attr_setdetachstate()
pthread_attr_getguardsize()
pthread_attr_setguardsize()
pthread_attr_getinheritsched()
pthread_attr_setinheritsched()
pthread_attr_getschedparam()
pthread_attr_setschedparam()
pthread_attr_getschedpolicy()
pthread_attr_setschedpolicy()
pthread_attr_getscope()
pthread_attr_setscope()
pthread_attr_getstackaddr()
pthread_attr_setstackaddr()
pthread_attr_getstacklazy()
pthread_attr_setstacklazy()
pthread_attr_getstacksize()
pthread_attr_setstacksize()Мы не станем подробно описывать все параметры потока, которые могут быть переопределены атрибутной записью, ведь для этого есть техническая документация QNX, а рассмотрим только наиболее интересные параметры.
Присоединенность
Это одно из самых интересных свойств потока, но одновременно и одно из самых сложных для понимания, поэтому есть смысл остановиться на нем более подробно. Поток может создаваться как ожидаемый (
PTHREAD_CREATE_JOINABLEPTHREAD_CREATE_DETACHED
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(NULL, &attr, &function, NULL);Присоединенный поток сохраняет некоторую связь с родителем (мы это рассмотрим, когда речь пойдет о завершения потока), в то время как отсоединенный поток после точки ветвления ведет себя как совершенно автономная сущность: после точки ветвления у родительского потока нет возможности синхронизироваться с его завершением, получить код его завершения или результат выполнения потока.
Можно ожидать завершения присоединенного потока в некотором другом потоке процесса (чаще всего именно в родительском, но это не обязательно) с помощью следующего вызова:
int pthread_join(pthread_t thread, void** value_ptr);где
threadpthread_create(pthread_t* thread, ...)pthread_self()
value_ptrNULLreturnpthread_exit()В API QNX присутствует родственная функция (не POSIX)
pthread_timedjoin()
int pthread_timedjoin(pthread_t thread, void** value_ptr,
const struct timespec* abstime);Таким образом, вызов
pthread_join()Значение
value_ptrNULLvalue_ptrPTHREAD_CANCELEDЕсли поток предназначен для выполнения автономной работы, не требует синхронизации и не предполагает возвращать значение, он может создаваться как отсоединенный. Поскольку таких случаев достаточно много, даже большинство (например, все множество параллельных сетевых серверов), то такое поведение потока вполне могло бы быть умалчиваемым при создании. Причина несколько ограниченного использования отсоединенных потоков относительно тех случаев, когда это может быть оправданным, состоит, скорее всего, в интуитивной боязни программистов «потерять контроль» над параллельно выполняемой ветвью, хотя зачастую этот контроль бывает чисто иллюзорным (принудительное завершение потока мы подробно рассмотрим позже).
По умолчанию потоки создаются именно как присоединенные, и это аргументируется тем обстоятельством, что такой поток всегда может сделать себя (или другой поток) отсоединенным, вызвав из своей функции потока:
int pthread_detach(pthread_t thread);Превратить же поток, созданный как отсоединенный, в присоединенный (ожидаемый) нет никакой возможности. Таким образом, это одностороннее преобразование!
Для отсоединенного потока все задействованные им системные ресурсы освобождаются в момент его завершения, а для ожидаемого — в момент выполнения
pthread_join()Пример синхронизации порожденных потоков:
const int THR_NUM = 5; // число дочерних потоков
pthread_t thrarray[THR_NUM];
for (int i = 0; i < THR_NUM, i++)
pthread_create(&thrarray[i], NULL, &thrfunc, NULL);
...
