Наши партнеры








Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

На главную -> MyLDP -> Электронные книги по ОС Linux
Цилюрик О.И. 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 

Предыдущий раздел: Оглавление Следующий раздел:
Завершение потока   Сигналы в потоках