На этом рисунке мы отмечаем, что структура 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. Поток номер readline
, используя и модифицируя свои собственные данные.
Один вопрос, который мы пока не рассмотрели, заключается в следующем: что происходит, когда поток завершает свое выполнение? Если поток вызвал функцию readline
, эта функция выделила в памяти область, которая должна быть освобождена по завершении выполнения потока. Для этого используется pthread_key_create
, одним из аргументов этой функции является указатель на pkey
для данного потока, вызывая соответствующую функцию-деструктор для каждого непустого указателя pkey
. Под «соответствующим деструктором» мы понимаем указатель на функцию, хранящийся в массиве Key
с рис. 26.2. Таким образом осуществляется освобождение памяти, занимаемой собственными данными потока, когда выполнение потока завершается.
Первые две функции, которые обычно вызываются при работе с собственными данными потока, — это pthread_once
и pthread_key_create
.
#include
int pthread_once(pthread_once_t *
int pthread_key_create(pthread_key_t *
Функция 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 */
}