Рейтинговые книги
Читем онлайн 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: разработка сетевых приложений - Уильям Стивенс бесплатно.
Похожие на UNIX: разработка сетевых приложений - Уильям Стивенс книги

Оставить комментарий