Мобильное программирование приложений реального времени в стандарте POSIX

       

Работа с индивидуальными данными потоков управления


Все потоки управления одного процесса разделяют общее адресное пространство и, следовательно, имеют общие данные. Чтобы сделать некоторые данные индивидуальными для потока, нужно принять специальные меры: с помощью функции pthread_key_create() создать ключ и ассоциировать с ним индивидуальные данные, воспользовавшись функцией pthread_setspecific(). В дальнейшем эти данные можно извлекать посредством функции pthread_getspecific(). Подчеркнем, что при обращении по одному (разделяемому) ключу разные потоки будут получать доступ к разным данным.

Создать один ключ, очевидно, нужно один раз. В ситуации, когда несколько потоков управления выполняют одну программу, это сопряжено с определенными проблемами. Стандартный прием, заключающийся во введении статической переменной, хранящей признак инициализированности (в данном случае – признак того, что ключ уже создан) в многопотоковой среде не работает, поскольку проверка и последующее изменение значения подобной переменной не являются атомарным действием. В принципе, манипуляции со статической переменной можно защитить каким-либо средством синхронизации, но его тоже нужно инициализировать!

Для решения проблемы однократного выполнения инициализирующих действий в многопотоковой среде стандарт POSIX-2001 предлагает функцию pthread_once() (см. листинг 1.19).

#include <pthread.h>

pthread_once_t once_control = PTHREAD_ONCE_INIT;

int pthread_once ( pthread_once_t *once_control_ptr, void (*init_routine) (void));

Листинг 1.19. Описание функции pthread_once(). (html, txt)

При первом и только при первом обращении к функции pthread_once() с фиксированным значением аргумента once_control_ptr, вне зависимости от того, какой из потоков процесса его выполняет, будет вызвана функция (*init_routine) (), которая по идее должна осуществлять инициализирующие действия, такие как создание ключа индивидуальных данных потоков управления.

Переменная, на которую указывает аргумент once_control_ptr, должна иметь начальное значение PTHREAD_ONCE_INIT и не должна быть автоматической.


Все потоки управления одного процесса разделяют общее адресное пространство и, следовательно, имеют общие данные. Чтобы сделать некоторые данные индивидуальными для потока, нужно принять специальные меры: с помощью функции pthread_key_create() создать ключ и ассоциировать с ним индивидуальные данные, воспользовавшись функцией pthread_setspecific(). В дальнейшем эти данные можно извлекать посредством функции pthread_getspecific(). Подчеркнем, что при обращении по одному (разделяемому) ключу разные потоки будут получать доступ к разным данным.

Создать один ключ, очевидно, нужно один раз. В ситуации, когда несколько потоков управления выполняют одну программу, это сопряжено с определенными проблемами. Стандартный прием, заключающийся во введении статической переменной, хранящей признак инициализированности (в данном случае – признак того, что ключ уже создан) в многопотоковой среде не работает, поскольку проверка и последующее изменение значения подобной переменной не являются атомарным действием. В принципе, манипуляции со статической переменной можно защитить каким-либо средством синхронизации, но его тоже нужно инициализировать!

Для решения проблемы однократного выполнения инициализирующих действий в многопотоковой среде стандарт POSIX-2001 предлагает функцию pthread_once() (см. листинг 1.19).

#include <pthread.h>

pthread_once_t once_control = PTHREAD_ONCE_INIT;

int pthread_once ( pthread_once_t *once_control_ptr, void (*init_routine) (void));

Листинг 1.19. Описание функции pthread_once().

При первом и только при первом обращении к функции pthread_once() с фиксированным значением аргумента once_control_ptr, вне зависимости от того, какой из потоков процесса его выполняет, будет вызвана функция (*init_routine) (), которая по идее должна осуществлять инициализирующие действия, такие как создание ключа индивидуальных данных потоков управления.

Переменная, на которую указывает аргумент once_control_ptr, должна иметь начальное значение PTHREAD_ONCE_INIT и не должна быть автоматической.




За создание и удаление ключа индивидуальных данных потоков управления, согласно стандарту POSIX-2001, отвечают функции pthread_key_create() и pthread_key_delete() (см. листинг 1.20).

#include <pthread.h>

int pthread_key_create ( pthread_key_t *key_ptr, void (*destructor) (void *));

int pthread_key_delete ( pthread_key_t key);

Листинг 1.20. Описание функций pthread_key_create() и pthread_key_delete(). (html, txt)

Функция pthread_key_create() создает новый ключ, которым могут воспользоваться все входящие в процесс потоки управления, и, в соответствии со "штабной дисциплиной", помещает его по указателю key_ptr. Сразу после создания ключа для всех потоков в качестве индивидуальных данных с ним ассоциируется значение NULL. (Аналогично, после создания потока управления он не имеет индивидуальных данных, поэтому со всеми доступными ему ключами также ассоциированы пустые указатели.)

С ключом может быть ассоциирован деструктор, который вызывается при завершении потока управления с индивидуальными данными в качестве аргумента. Обычно в роли индивидуальных данных выступает указатель на динамически зарезервированную потоком область памяти, которую деструктор должен освободить.

Функция pthread_key_delete() удаляет заданный ключ, не заботясь о том, пусты ли ассоциированные значения, и не вызывая каких-либо деструкторов (напротив, ее обычно вызывают из деструктора). Вся ответственность по освобождению памяти и выполнению других необходимых зачисток возлагается на приложение.

Для выборки и изменения ассоциированных с ключом key индивидуальных данных вызывающего потока управления предназначены функции pthread_getspecific() и pthread_setspecific() (см. листинг 1.21).

#include <pthread.h>

void *pthread_getspecific ( pthread_key_t key);

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

Листинг 1.21. Описание функций pthread_getspecific() и pthread_setspecific(). (html, txt)

Функция pthread_getspecific() возвращает индивидуальные данные в качестве результата; функция pthread_setspecific() ассоциирует с ключом key значение аргумента value.

На листинге 1.22 показана программа, использующая стандартную схему создания ключа и манипулирования индивидуальными данными потоков управления.

Листинг 1.22. Пример программы, формирующей и опрашивающей индивидуальные данные потоков управления. (html, txt)

Результат работы этой программы может выглядеть так, как показано на листинге 1.23.

Время начала операций потока управления: 1075707670 сек, 584737 мсек

Листинг 1.23. Возможные результаты работы программы, формирующей и опрашивающей индивидуальные данные потоков управления. (html, txt)



За создание и удаление ключа индивидуальных данных потоков управления, согласно стандарту POSIX-2001, отвечают функции pthread_key_create() и pthread_key_delete() (см. листинг 1.20).

#include <pthread.h>

int pthread_key_create ( pthread_key_t *key_ptr, void (*destructor) (void *));

int pthread_key_delete ( pthread_key_t key);

Листинг 1.20. Описание функций pthread_key_create() и pthread_key_delete().

Функция pthread_key_create() создает новый ключ, которым могут воспользоваться все входящие в процесс потоки управления, и, в соответствии со "штабной дисциплиной", помещает его по указателю key_ptr. Сразу после создания ключа для всех потоков в качестве индивидуальных данных с ним ассоциируется значение NULL. (Аналогично, после создания потока управления он не имеет индивидуальных данных, поэтому со всеми доступными ему ключами также ассоциированы пустые указатели.)

С ключом может быть ассоциирован деструктор, который вызывается при завершении потока управления с индивидуальными данными в качестве аргумента. Обычно в роли индивидуальных данных выступает указатель на динамически зарезервированную потоком область памяти, которую деструктор должен освободить.

Функция pthread_key_delete() удаляет заданный ключ, не заботясь о том, пусты ли ассоциированные значения, и не вызывая каких-либо деструкторов (напротив, ее обычно вызывают из деструктора). Вся ответственность по освобождению памяти и выполнению других необходимых зачисток возлагается на приложение.

Для выборки и изменения ассоциированных с ключом key индивидуальных данных вызывающего потока управления предназначены функции pthread_getspecific() и pthread_setspecific() (см. листинг 1.21).

#include <pthread.h>

void *pthread_getspecific ( pthread_key_t key);

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

Листинг 1.21. Описание функций pthread_getspecific() и pthread_setspecific().

Функция pthread_getspecific() возвращает индивидуальные данные в качестве результата; функция pthread_setspecific() ассоциирует с ключом key значение аргумента value.



На листинге 1. 22 показана программа, использующая стандартную схему создания ключа и манипулирования индивидуальными данными потоков управления.

/* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Программа запоминает в качестве индивидуальных данных */ /* потока управления время начала активных операций */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <sys/time.h> static pthread_key_t data_key; static pthread_once_t key_once = PTHREAD_ONCE_INIT;

/* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Деструктор индивидуальных данных, в роли которых */ /* выступает указатель на структуру типа timeval. */ /* Поскольку она не содержит указателей, достаточно */ /* освободить занимаемую ею память */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ static void data_destructor (void *p) { free (p); }

/* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Функция создания ключа индивидуальных данных, */ /* ассоциирующая с ним деструктор, освобождающий память */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ static void create_data_key (void) { (void) pthread_key_create (&data_key, data_destructor); }

/* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Функция инициализации индивидуальных данных. */ /* Запрашивает астрономическое время */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ void *start_func (void) { struct timeval *tmvl_ptr;

/* Запомним астрономическое время начала операций */ /* потока управления */ if ((tmvl_ptr = (struct timeval *) malloc (sizeof (struct timeval))) == NULL) { return (NULL); } (void) gettimeofday (tmvl_ptr, NULL);

/* Создадим ключ индивидуальных данных, перепоручив */ /* вызов pthread_key_create() функции pthread_once() */ (void) pthread_once (&key_once, create_data_key);

(void) pthread_setspecific (data_key, tmvl_ptr); return (tmvl_ptr); }

/* * * * * * * * * * * * * * * * * * * * * * * */ /* Функция main() вызывает функцию инициализации */ /* и запрашивает индивидуальные данные потока */ /* управления */ /* * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { struct timeval *tmvl_ptr;



if (start_func () == NULL) { return (1); }

if ((tmvl_ptr = (struct timeval *) pthread_getspecific (data_key)) != NULL) { printf (" Время начала операций потока управления: " "%ld сек, %ld мсек\n", tmvl_ptr->tv_sec, tmvl_ptr->tv_usec); } else { printf ("Отсутствуют индивидуальные данные потока " "управления.\n"); printf ("Время начала операций неизвестно\n"); return (2); }

return 0; }

Листинг 1.22. Пример программы, формирующей и опрашивающей индивидуальные данные потоков управления.

Результат работы этой программы может выглядеть так, как показано на листинге 1.23.

Время начала операций потока управления: 1075707670 сек, 584737 мсек

Листинг 1.23. Возможные результаты работы программы, формирующей и опрашивающей индивидуальные данные потоков управления.


Содержание раздела