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