Новосибирский Государственный Университет
Опубликован: 28.04.2010 | Доступ: свободный | Студентов: 685 / 60 | Оценка: 4.62 / 4.24 | Длительность: 10:21:00
Лекция 3:

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

< Лекция 2 || Лекция 3: 1234 || Лекция 4 >
Аннотация: В ходе этого раздела вы изучите: создание нитей с атрибутами по умолчанию, передачу параметров нити, завершение нити, ожидание завершения другой нити, принудительное завершение нити, обработку принудительного завершения нити.

Если темы там возникнут – сразу снять

Бить нельзя их, а вот не вникнут - разъяснять

В. Высоцкий

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С), он может не дождаться завершения своих потомков, а это приведет либо к утечке памяти, либо к висячим ссылкам.

В программах с небольшим количеством нитей часто передают в качестве параметра указатели на статические переменные, но в больших программах с большим числом нитей это неприемлемо.

< Лекция 2 || Лекция 3: 1234 || Лекция 4 >