Создание и завершение нитей Программирование с использованием POSIX thread library Иртегов Д.В. Учебное пособие подготовлено по заказу и при поддержке ООО «Сан Майкросистемс СПБ»
В ходе этой лекции вы изучите Создание нитей с атрибутами по умолчанию Передачу параметров нити Завершение нити Ожидание завершения другой нити Принудительное завершение нити Обработку принудительного завершения нити
pthread_create(3C) #include int pthread_create( pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void*), void *restrict arg); ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ Код ошибки, 0 при успешном завершении
Параметры pthread_t * thread – Выходной параметр. Указатель на переменную, в которой при успешном завершении будет размещен идентификатор нити. const pthread_attr_t * attr – Входной параметр. Указатель на структуру, в которой заданы атрибуты нити (рассматривается на следующей лекции). Если этот указатель равен NULL, используются атрибуты по умолчанию. void *(*start_routine)(void*) – Входной параметр. Указатель на функцию, которая будет запущена во вновь созданной нити. void * arg – Входной параметр. Значение, которое будет передано в качестве параметра start_routine.
pthread_t Непрозрачный тип В Solaris – небольшие целые числа В Linux 2.4 – pid В Linux 2.6 – указатель (необходимо проявлять осторожность при работе с неинициализированным pthread_t или идентификаторами завершенных нитей)
Передача параметров нити Параметр имеет тип void * Система никогда не обращается к нему как к указателю (через него можно передавать скаляры) При передаче параметров есть две опасности: –Утечка памяти –Висячие ссылки
Выделение памяти под параметры в стеке Следует проявлять осторожность при передаче параметров, размещенных в стеке Это можно делать только если родительская нить делает pthread_join(3C) (иначе есть риск, что родительская нить завершится раньше, чем дочерняя доберется до параметров)
Выделение памяти под параметры через malloc(3C) Нить должна либо освобождать блок параметров сама –Дурной тон (считается неправильным, когда функция знает, как выделялась память под ее параметры) Либо возвращать его в коде возврата (тогда родитель должен делать pthread_join(3C) и освобождать ее)
Выделение памяти под параметры статически Проще всего Ограничивает количество нитей, которые вы можете создать –Не всегда приемлемо
Pthread_exit(3C) #include void pthread_exit(void *value_ptr);
Pthread_exit(3C) В основном эквивалентна return val; в start_routine В некоторых комбинациях libpthread/C++ компилятора не вызывает деструкторы локальных переменных (хотя по идее должна вызывать)
exit(2) в многопоточной программе exit(2) завершает процесс (все нити) return val; в main эквивалентен exit(2) Если хотите, чтобы нити вашей программы исполнялись после завершения main, необходимо завершать main по pthread_exit(3C)
exit(2) и С++ exit(2) вызывает деструкторы статических переменных и обработчики atexit(3C) При этом во многих реализациях С++ нити еще продолжают работать. –Обращение к статическим переменным приведет к проблемам вплоть до SIGSEGV _exit(2) не вызывает деструкторы и atexit(3C)
pthread_join(3C) #include int pthread_join( pthread_t thread, void **status); status==NULL – игнорировать код возврата
Ожидание завершения нити Любая нить может ждать завершения любой нити того же процесса (в отличие от wait(2), который может делать только родитель) К моменту разблокировки pthread_join(3C), стек и TLD уже уничтожены Если несколько нитей ждут одну, только одна из них получает код возврата, остальные – ESRCH Если нить ждет сама себя, ошибка EDEADLK
pthread_detach(3C) #include int pthread_detach( pthread_t thread); Можно запускать нить отсоединенной (атрибут detachstate)
pthread_cancel(3C) #include int pthread_cancel( pthread_t target_thread); int pthread_setcancelstate( int state, int *oldstate); int pthread_setcanceltype( int type, int *oldtype);
Cancel state/type State (pthread_setcancelstate(3C)) –PTHREAD_CANCEL_ENABLE (разрешено) –PTHREAD_CANCEL_DISABLE (запрещено) Type (pthread_setcanceltype(3C)) –PTHREAD_CANCEL_ASYNCHRONOUS (в любой момент) –PTHREAD_CANCEL_DEFERRED (только в точках прерывания)
Точки прерывания Man cancellation(5) pthread_testcancel(3C) aio_suspend(3RT), close(2), creat(2), getmsg(2), getpmsg(2), lockf(3C), mq_receive(3RT), mq_send(3RT), msgrcv(2), msgsnd(2), msync(3C), nanosleep(3RT), open(2), pause(2), poll(2), pread(2), pthread_cond_timedwait(3C), pthread_cond_wait(3C), pthread_join(3C), pthread_testcancel(3C), putmsg(2), putpmsg(2), pwrite(2), read(2), readv(2), select(3C), sem_wait(3RT), sigpause(3C), sigwaitinfo(3RT), sigsuspend(2), sigtimedwait(3RT), sigwait(2), sleep(3C), sync(2), system(3C), tcdrain(3C), usleep(3C), wait(3C), waitid(2), wait3(3C), waitpid(3C), write(2), writev(2), fcntl(2) (с командой F_SETLKW) pthread_mutex_lock(3C) не является точкой прерывания
pthread_cleanup_push(3C)/pop #include void pthread_cleanup_push( void (*handler, void *), void *arg); void pthread_cleanup_pop( int execute);
pthread_cleanup_push(3C)/pop Должны использоваться парами в пределах одного блока В действительности, это макроопределения, содержащие { и } Использование setjmp(3C)/longjmp(3C) в сочетании с pthrhead_cleanup_push/pop приводит к непредсказуемым последствиям –(возможно, к разрушению стека и SIGSEGV) –В лучшем случае – к нарушению порядка вызова обработчиков