Ваши аргументы верны. Эти вызовы предназначены для данных, зависящих от потока. Это способ дать каждому потоку «глобальную» область, где он может хранить то, что ему нужно, но только если он ему нужен.
Ключ разделяется между всеми потоками, поскольку он создан с помощью pthread_once()
при первом его использовании, но значение, присвоенное этому ключу, отличается для каждого потока (если только оно не установлено в NULL). Имея значение a void*
в блок памяти, поток, который нуждается в данных, связанных с потоком, может выделить его и сохранить адрес для последующего использования. И потоки, которые не называют подпрограммой, которая требует данных, зависящих от потока, никогда не теряют память, поскольку она никогда не выделяется для них.
Одна из областей, где я их использовал, заключается в том, чтобы сделать стандартную библиотеку C потоковой безопасностью. Функция strtok()
(в отличие от поточно-безопасного strtok_r()
, который считался мерзостью, когда мы это делали), в реализации, в которой я был задействован, использовался почти этот точно такой же код при первом его вызове, чтобы выделить некоторую память, которая была бы используется strtok()
для хранения информации для последующих вызовов. Эти последующие вызовы будут извлекать данные, зависящие от потока, для продолжения токенизации строки без вмешательства в другие потоки, выполняющие то же самое.
Это означало, что пользователям библиотеки не нужно было беспокоиться о перекрестных помехах между потоками - они все равно должны были обеспечить, чтобы один поток не вызывал функцию до тех пор, пока последняя не закончилась, но это то же самое, что и с одиночным - исправленный код.
Это позволило нам дать «правильную» среду C каждому потоку, запущенному в нашей системе, без обычных «вы должны называть эти специальные нестандартные процедуры повторного входа», которые другие продавцы налагают на своих пользователей.
Что касается реализации, из того, что я помню в потоках пользовательского режима DCE (который, я думаю, был предшественником текущих pthreads), каждый поток имел одну структуру, в которой хранились такие вещи, как указатели команд, указатели стека, содержимое регистров и скоро. Было очень просто добавить один указатель на эту структуру для достижения очень мощных функций с минимальными затратами. Указатель указывал на массив (связанный список в некоторых реализациях) пар ключ/указатель, чтобы каждый поток мог иметь несколько ключей (например, один для strtok()
, один для rand()
).