Создание и завершение нитей

Download Report

Transcript Создание и завершение нитей

Создание и завершение
нитей
Программирование с использованием
POSIX thread library
2006-2007 Иртегов Д.В.
Учебное пособие подготовлено по заказу и при поддержке
ООО «Сан Майкросистемс СПБ»
В ходе этой лекции вы изучите
• Создание нитей с атрибутами по
умолчанию
• Передачу параметров нити
• Завершение нити
• Ожидание завершения другой нити
• Принудительное завершение нити
• Обработку принудительного
завершения нити
pthread_create(3C)
#include <pthread.h>
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 <pthread.h>
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 <pthread.h>
int pthread_join(
pthread_t thread,
void **status);
status==NULL – игнорировать код
возврата
Ожидание завершения нити
• Любая нить может ждать завершения любой
нити того же процесса (в отличие от wait(2),
который может делать только родитель)
• К моменту разблокировки pthread_join(3C),
стек и TLD уже уничтожены
• Если несколько нитей ждут одну, только одна
из них получает код возврата, остальные –
ESRCH
• Если нить ждет сама себя, ошибка EDEADLK
pthread_detach(3C)
#include <pthread.h>
int pthread_detach(
pthread_t thread);
Можно запускать нить отсоединенной
(атрибут detachstate)
pthread_cancel(3C)
#include <pthread.h>
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 <pthread.h>
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)
– В лучшем случае – к нарушению порядка вызова
обработчиков