Библиотека сайта rus-linux.net
| Цилюрик О.И. Linux-инструменты для Windows-программистов | ||
| Назад | Библиотеки API POSIX | Вперед |
Данные потока
Собственные данные потока
Техника создания собственных данных потоков (thread specific data)
создаёт по одному экземпляру каждого вида данных. Стандарт POSIX
указывает, что это число видов данных (тип pthread_key_t)
не превышает 128. Последовательность действий при создании TSD:
1. Поток запрашивает pthread_key_create()
для создания ключа доступа к блоку данных определённого
типа; если потоку нужно иметь несколько блоков данных разной
типизациии (назначения), он делает такой вызов нужное число раз.
2. Некоторая сложность здесь в том, что запросить
распределение ключа должен только один поток, первым достигший
точки распределения. Последующие потоки должны только воспользоваться
ранее распределённым значением ключа. Для разрешения этой сложности
вводится вызов pthread_once().
3. Теперь каждый поток, использующий блок данных,
должен запросить специфический экземпляр данных по
pthread_getspecific() и, если он убеждается, что это NULL,
то запросить распределение блока для этого значения ключа по
pthread_setspecific().
4. В дальнейшем поток (и все вызываемые из него функции) может
работать со своим экземпляром, запрашивая его по pthread_getspecific().
5. При завершении любого потока система уничтожает и
его экземпляр данных. При этом вызывается деструктор пользователя,
который устанавливается при создании ключа pthread_key_create().
Деструктор единый для всех экземпляров данных во всех потоках для
этого значения ключа (pthread_key_t),
но он получает параметром значение указателя на экземпляр данных завершаемого потока.
Всё это гораздо легче показать на примере кода:
static pthread_key_t key;
static pthread_once_t once = PTHREAD_ONCE_INIT;
typedef struct data_bloc { // наш собственный тип данных
//...
} data_t;
static void destructor( data_t *db ) { // деструктор собственных данных
free( db );
}
static void once_creator( void ) { // создаёт единый на процесс ключ для данных data_t
pthread_key_create( &key, destructor );
}
void* thread_proc( void *data ) { // функция потока
pthread_once( &once, once_creator ); // гарантия единичности создания ключа
if( pthread_getspecific( key ) == NULL )
pthread_setspecific( key, malloc( sizeof( data_t ) ) );
// теперь везде в вызываемых потоком функциях:
data_t *db = pthread_getspecific( key );
// ...
}
Далее пример показывает разного рода данные, используемые потоком: а). параметр, передаваемый функции потока в стеке (и точно так же локальные данные функции потока), б). глобальные данные доступные всем потокам, в). экземпляр собственных данных потока.
own.c :
include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
static int global = 0;
static pthread_key_t key;
typedef struct data_bloc { // наш собственный тип данных
pthread_t tid;
} data_t;
void put_msg( int param ) {
printf( "global=%u , parameter=%u , own=%lu\n",
global, param, ((data_t*)pthread_getspecific( key ))->tid );
}
static pthread_once_t once = PTHREAD_ONCE_INIT;
static void destructor( void* db ) { // деструктор собственных данных
data_t *p = (data_t*)db;
free( p );
}
static void once_creator( void ) { // создаёт единый на процесс ключ для данных data_
pthread_key_create( &key, destructor );
}
void* thread_proc( void *data ) { // функция потока
int param = (int)data;
global++;
pthread_once( &once, once_creator ); // гарантия единичности создания ключа
pthread_setspecific( key, malloc( sizeof( data_t ) ) );
data_t *db = pthread_getspecific( key );
db->tid = pthread_self();
put_msg( param );
return NULL;
}
int main( int argc, char **argv, char **envp ) {
#define TCNT 5
pthread_t tid[ TCNT ];
int i;
for( i = 0; i < TCNT; i++ )
pthread_create( &tid[ i ], NULL, thread_proc, (void*)( i + 1 ) );
for( i = 0; i < TCNT; i++ ).
pthread_join( tid[ i ], NULL );
return( EXIT_SUCCESS );
}
... и весьма неожиданные и поучительные результаты выполнения такого примера (два последовательно выполненные прогона, которые существенно отличаются выполнением):
$ ./own global=1 , parameter=4 , own=3047005040 global=2 , parameter=3 , own=3057494896 global=3 , parameter=1 , own=3078474608 global=4 , parameter=5 , own=3036515184 global=5 , parameter=2 , own=3067984752 $ ./own global=4 , parameter=1 , own=3078527856 global=5 , parameter=4 , own=3042863984 global=4 , parameter=3 , own=3057548144 global=4 , parameter=2 , own=3068038000 global=5 , parameter=5 , own=3030383472
| Предыдущий раздел: | Оглавление | Следующий раздел: |
| Завершение потока | Сигналы в потоках |
