Создание и завершение нитей
Если темы там возникнут – сразу снять
Бить нельзя их, а вот не вникнут - разъяснять
1. Создание нитей с атрибутами по умолчанию
В POSIX Thread API нить создается библиотечной функцией pthread_create(3C).
Параметры этой функции:
- pthread_t * thread – Выходной параметр. Указатель на переменную, в которой при успешном завершении будет размещен идентификатор нити.
- const pthread_attr_t * attr – Входной параметр. Указатель на структуру, в которой заданы атрибуты нити (рассматривается на следующей лекции). Если этот указатель равен
- NULL, используются атрибуты по умолчанию.
- void *(*start_routine)(void*) – Входной параметр. Указатель на функцию, которая будет запущена во вновь созданной нити.
- void *arg – Входной параметр. Значение,которое будет передано в качестве параметра start_routine.
- 0 при успешном завершении
- Код ошибки при неудачном завершении
Большинство других функций POSIX Threads API используют аналогичное соглашение о кодах возврата. Если в нашем учебном пособии у функции не указано описание кода возврата, значит, что она возвращает 0 при успешном завершении и код ошибки при ошибке. В страницах man(1) всегда указывается точное описание кода возврата всех функций.
Коды ошибок
- Значения кодов ошибок определены в виде символов препроцессора в файле errno.h
- EAGAIN – системе не хватает ресурсов для создания нити. Возможно, не хватает памяти под стек, исчерпан архитектурный лимит на количество нитей в процессе ( PTHREAD_THREADS_MAX ) либо административное ограничение на количество нитей. Как и у остальных системных вызовов, код EAGAIN означает, что повторный вызов функции с теми же параметрами может не привести к ошибке.
- EINVAL – один из параметров имеет недопустимое значение. Например, указатель на start_routine указывает на страницу памяти, исполнение которой запрещено.
- EPERM – вы не имеете полномочий для исполнения нити с заданными атрибутами. Например, вы не можете установить заданные в структуре attr класс планирования или приоритет.
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <string.h> void * thread_body(void * param) { sleep(1); printf("Child\n"); } /* * creates thread with default attributes and no parameters * sleeps enough time to ensure that thread will print his message * (under reasonable conditions) */ int main(int argc, char *argv[]) { pthread_t thread; int code; int i; code=pthread_create(&thread, NULL, thread_body, NULL); if (code!=0) { char buf[256]; strerror_r(code, buf, sizeof buf); fprintf(stderr, "%s: creating thread: %s\n", argv[0], buf); exit(1); } sleep(5); return (EXIT_SUCCESS); }3.1.
Параметр функции нити описан как void *, но библиотека никогда не пытается обращаться к нему как к указателю. Поэтому этот указатель можно использовать либо как ссылку на структуру (блок параметров нити), либо для передачи скалярного значения.
При передаче структур данных в качестве параметра нужно проявлять осторожность.
Во первых, не следует передавать структуры данных, размещенные в стеке родительской нити, то есть переменные с классом памяти auto и блоки памяти, размещенные при помощи alloca(3C). Действительно, если ваша нить вернет управление из текущей функции или вообще завершится до того, как созданная нить начнет работать с блоком параметров, получится, что вы передали в качестве параметра висячую ссылку.
Во вторых, при передаче структур данных, размещенных при помощи malloc(3C) или оператора new языка C++, необходимо решить вопрос о том, кто будет освобождать эту структуру. Если структура не будет освобождена при помощи free(3C) или оператора delete, это приведет к утечке памяти. Существует несколько решений этого вопроса, приемлемых в разных ситуациях.
Одно из решений состоит в том, что родитель размещает структуру, а созданная нить ее освобождает. Но обычно это считается дурным тоном при программировании на C/C++ (хорошим тоном считается, чтобы вызываемая функция не знала, каким образом выделена память под переданные ей параметры).
Другое решение состоит в том, что родитель размещает структуру, дожидается завершения потомка при помощи вызова pthread_join(3C) и освобождает структуру. В рамках этого подхода можно передавать как структуры данных, созданные при помощи malloc(3C), так и структуры данных, размещенные в стеке родителя. Проблема этого подхода состоит в том, что если родитель будет принудительно завершен при помощи pthread_cancel(3С), он может не дождаться завершения своих потомков, а это приведет либо к утечке памяти, либо к висячим ссылкам.
В программах с небольшим количеством нитей часто передают в качестве параметра указатели на статические переменные, но в больших программах с большим числом нитей это неприемлемо.