2009-02-14 4 views
4

Я не уверен в том, как работает pthread dataspecific: учитывая следующий код (найденный в Интернете), это означает, что я могу создать, например, 5 потоков в главном, иметь вызов func только в некоторых из них (давайте скажем, 2) эти потоки будут иметь ключ «данных» для данных (ptr = malloc (OBJECT_SIZE)), а другие потоки будут иметь тот же самый ключ, но с NULL значением?C: Как работает pthread dataspecific?

static pthread_key_t key; 
static pthread_once_t key_once = PTHREAD_ONCE_INIT; 

static void 
make_key() 
{ 
    (void) pthread_key_create(&key, NULL); 
} 

func() 
{ 
    void *ptr; 

    (void) pthread_once(&key_once, make_key); 
    if ((ptr = pthread_getspecific(key)) == NULL) { 
    ptr = malloc(OBJECT_SIZE); 
    ... 
    (void) pthread_setspecific(key, ptr); 
    } 
    ... 
} 

Некоторые пояснения о том, как dataspecific работает и как это может быть реализовано в PTHREAD (простой способ) был бы признателен!

ответ

6

Ваши аргументы верны. Эти вызовы предназначены для данных, зависящих от потока. Это способ дать каждому потоку «глобальную» область, где он может хранить то, что ему нужно, но только если он ему нужен.

Ключ разделяется между всеми потоками, поскольку он создан с помощью pthread_once() при первом его использовании, но значение, присвоенное этому ключу, отличается для каждого потока (если только оно не установлено в NULL). Имея значение a void* в блок памяти, поток, который нуждается в данных, связанных с потоком, может выделить его и сохранить адрес для последующего использования. И потоки, которые не называют подпрограммой, которая требует данных, зависящих от потока, никогда не теряют память, поскольку она никогда не выделяется для них.

Одна из областей, где я их использовал, заключается в том, чтобы сделать стандартную библиотеку C потоковой безопасностью. Функция strtok() (в отличие от поточно-безопасного strtok_r(), который считался мерзостью, когда мы это делали), в реализации, в которой я был задействован, использовался почти этот точно такой же код при первом его вызове, чтобы выделить некоторую память, которая была бы используется strtok() для хранения информации для последующих вызовов. Эти последующие вызовы будут извлекать данные, зависящие от потока, для продолжения токенизации строки без вмешательства в другие потоки, выполняющие то же самое.

Это означало, что пользователям библиотеки не нужно было беспокоиться о перекрестных помехах между потоками - они все равно должны были обеспечить, чтобы один поток не вызывал функцию до тех пор, пока последняя не закончилась, но это то же самое, что и с одиночным - исправленный код.

Это позволило нам дать «правильную» среду C каждому потоку, запущенному в нашей системе, без обычных «вы должны называть эти специальные нестандартные процедуры повторного входа», которые другие продавцы налагают на своих пользователей.

Что касается реализации, из того, что я помню в потоках пользовательского режима DCE (который, я думаю, был предшественником текущих pthreads), каждый поток имел одну структуру, в которой хранились такие вещи, как указатели команд, указатели стека, содержимое регистров и скоро. Было очень просто добавить один указатель на эту структуру для достижения очень мощных функций с минимальными затратами. Указатель указывал на массив (связанный список в некоторых реализациях) пар ключ/указатель, чтобы каждый поток мог иметь несколько ключей (например, один для strtok(), один для rand()).

1

Ответ на ваш первый вопрос: да. Проще говоря, он позволяет каждому потоку выделять и сохранять свои собственные данные. Это примерно эквивалентно тому, что каждый поток просто распределяет и передает свою собственную структуру данных. API избавляет вас от необходимости передавать нить-локальную структуру ко всем подфункциям и позволяет вам искать ее по требованию.

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

Вы можете считать это двухуровневым хэшмапом. Ключ определяет, какая нить-локальная «переменная», к которой вы хотите получить доступ, и второй уровень может выполнять поиск нити-идентификатора для запроса значения для потока.