Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » UNIX: разработка сетевых приложений - Уильям Стивенс

UNIX: разработка сетевых приложений - Уильям Стивенс

Читать онлайн UNIX: разработка сетевых приложений - Уильям Стивенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 190 191 192 193 194 195 196 197 198 ... 263
Перейти на страницу:

На этом рисунке мы отмечаем, что структура Pthread поддерживается системой (вероятно, библиотекой потоков), но фактически собственные данные потока, которые мы размещаем в памяти с помощью функции malloc, поддерживаются нашей функцией (в данном случае readline). Все, что делает функция pthread_setspecific, — это установка указателя для данного ключа в структуре Pthread на выделенную область памяти. Аналогично, действие функции pthread_getspecific сводится к возвращению этого указателя.

4. Другой поток, например поток с номером n, вызывает функцию readline, возможно, в тот момент, когда поток с номером 0 все еще находится в стадии выполнения функции readline.

Функция readline вызывает функцию pthread_once, чтобы инициализировать ключ этого элемента собственных данных, но так как эта функция уже была однажды вызвана, то больше она не выполняется.

5. Функция readline вызывает функцию pthread_getspecific для получения значения указателя pkey[1] для данного потока, но возвращается пустой указатель. Тогда поток вызывает функцию malloc и функцию pthread_setspecific, как и в случае с потоком номер 0, инициализируя элемент собственных данных потока, соответствующий этому ключу (1). Этот процесс иллюстрирует рис. 26.5.

Рис. 26.5. Структуры данных после того, как поток n инициализировал свои собственные данные

6. Поток номер n продолжает выполнять функцию readline, используя и модифицируя свои собственные данные.

Один вопрос, который мы пока не рассмотрели, заключается в следующем: что происходит, когда поток завершает свое выполнение? Если поток вызвал функцию readline, эта функция выделила в памяти область, которая должна быть освобождена по завершении выполнения потока. Для этого используется указатель-деструктор, показанный на рис. 26.2. Когда поток, создающий элемент собственных данных, вызывает функцию pthread_key_create, одним из аргументов этой функции является указатель на функцию-деструктор. Когда выполнение потока завершается, система перебирает массив pkey для данного потока, вызывая соответствующую функцию-деструктор для каждого непустого указателя pkey. Под «соответствующим деструктором» мы понимаем указатель на функцию, хранящийся в массиве Key с рис. 26.2. Таким образом осуществляется освобождение памяти, занимаемой собственными данными потока, когда выполнение потока завершается.

Первые две функции, которые обычно вызываются при работе с собственными данными потока, — это pthread_once и pthread_key_create.

#include <pthread.h>

int pthread_once(pthread_once_t *onceptr, void (*init)(void));

int pthread_key_create(pthread_key_t *keyptr, void (*destructor)(void *value));

Обе функции возвращают: 0 в случае успешного выполнения, положительное значение Exxx в случае ошибки

Функция pthread_once обычно вызывается при вызове функции, манипулирующей собственными данными потока, но pthread_once использует значение переменной, на которую указывает onceptr, чтобы гарантировать, что функция init вызывается для каждого процесса только один раз.

Функция pthread_key_create должна вызываться только один раз для данного ключа в пределах одного процесса. Значение ключа возвращается с помощью указателя keyptr, а функция-деструктор (если аргумент является непустым указателем) будет вызываться каждым потоком по завершении его выполнения, если этот поток записывал какое-либо значение, соответствующее этому ключу.

Обычно эти две функции используются следующим образом (если игнорировать возвращение ошибок):

pthread_key_t rl_key;

pthread_once_t rl_once = PTHREAD_ONCE_INIT;

void readline_destructor(void *ptr) {

 free(ptr);

}

void readline_once(void) {

 pthread_key_create(&rl_key, readline_destructor);

}

ssize_t readline(...) {

 ...

 pthread_once(&rl_once, readline_once);

 if ((ptr = pthread_getspecific(rl_key)) == NULL) {

  ptr = Malloc(...);

  pthread_setspecifiс(rl_key, ptr);

  /* инициализация области памяти, на которую указывает ptr */

 }

 ...

 /* используются значения, на которые указывает ptr */

}

Каждый раз, когда вызывается функция readline, она вызывает функцию pthread_once. Эта функция использует значение, на которое указывает ее аргумент-указатель onceptr (содержащийся в переменной rl_once), чтобы удостовериться, что функция init вызывается только один раз. Функция инициализации readline_once создает ключ для собственных данных потока, который хранится в rl_key и который функция readline затем использует в вызовах функций pthread_getspecific и pthread_setspecific.

Функции pthread_getspecific и pthread_setspecific используются для того, чтобы получать и задавать значение, ассоциированное с данным ключом. Это значение представляет собой тот указатель, который показан на рис. 26.3. На что указывает этот указатель — зависит от приложения, но обычно он указывает на динамически выделяемый участок памяти.

#include <pthread.h>

void *pthread_getspecific(pthread_key_t key);

Возвращает: указатель на собственные данные потока (возможно, пустой указатель)

int pthread_setspecific(pthread_key_t key, const void *value);

Возвращает: 0 в случае успешного выполнения, положительное значение Exxx в случае ошибки

Обратите внимание на то, что аргументом функции pthread_key_create является указатель на ключ (поскольку эта функция хранит значение, присвоенное ключу), в то время как аргументами функций get и set являются сами ключи (которые, скорее всего, представляют собой небольшие целые числа, как уже говорилось).

Пример: функция readline, использующая собственные данные потока

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

В листинге 26.5 показана первая часть функции: переменные pthread_key_t и pthread_once_t, функции readline_destructor и readline_once и наша структура Rline, которая содержит всю информацию, нужную нам для каждого потока.

Листинг 26.5. Первая часть функции readline, безопасной в многопоточной среде

//threads/readline.c

 1 #include "unpthread.h"

 2 static pthread_key_t rl_key;

 3 static pthread_once_t rl_once = PTHREAD_ONCE_INIT;

 4 static void

 5 readline_destructor(void *ptr)

 6 {

 7  free(ptr);

 8 }

9 static void

10 readline_once(void)

11 {

12  Pthread_key_create(&rl_key, readline_destructor);

13 }

14 typedef struct {

15  int rl_cnt;      /* инициализируется нулем */

16  char *rl_bufptr; /* инициализируется значением rl_buf */

17  char rl_buf[MAXLINE];

18 } Rline;

Деструктор

4-8 Наша функция-деструктор просто освобождает всю память, которая была выделена для данного потока.

«Одноразовая» функция

9-13 Мы увидим, что наша «одноразовая» (то есть вызываемая только один раз) функция вызывается однократно из функции pthread_once и создает ключ, который затем используется в функции readline.

Структура Rline

14-18 Наша структура Rline содержит три переменные, которые, будучи объявленными как статические (static) в листинге 3.12, привели к возникновению описанных далее проблем. Такая структура динамически выделяется в памяти для каждого потока, а по завершении выполнения этого потока она освобождается функцией-деструктором.

В листинге 26.6 показана сама функция readline, а также функция my_read, которую она вызывает. Этот листинг является модификацией листинга 3.12.

Листинг 26.6. Вторая часть функции readline, безопасной в многопоточной среде

//threads/readline.c

19 static ssize_t

20 my_read(Rline *tsd, int fd, char *ptr)

21 {

22  if (tsd->rl_cnt <= 0) {

23 again:

24   if ((tsd->rl_cnt = read(fd, tsd->rl_buf, MAXLINE)) < 0) {

25    if (errno == EINTR)

26     goto again;

27    return (-1);

28   } else if (tsd->rl_cnt == 0)

29    return (0);

30   tsd->rl_bufptr = tsd->rl_buf;

31  }

32  tsd->rl_cnt--;

33  *ptr = *tsd->rl_bufptr++;

34  return (1);

1 ... 190 191 192 193 194 195 196 197 198 ... 263
Перейти на страницу:
Тут вы можете бесплатно читать книгу UNIX: разработка сетевых приложений - Уильям Стивенс.
Комментарии