Beginner
pthread_self() / pthread_equal()
TLPI – Ch 29.5
Just as every process has a unique process ID (PID), every thread within a process has a unique thread ID (TID). Thread IDs are the primary handle you use to refer to a specific thread in Pthreads function calls. This part explains how to get a thread’s own ID, how to compare IDs, and an important distinction between POSIX thread IDs and the kernel-level thread IDs you may encounter in tools like top or /proc.
1. Why Thread IDs Are Necessary
Thread IDs serve two main purposes in Pthreads programming:
Many Pthreads functions require a thread ID to know which thread to act on. Examples:
pthread_join(tid, ...)— wait for a threadpthread_detach(tid)— detach a threadpthread_cancel(tid)— cancel a threadpthread_kill(tid, sig)— send a signal
In some applications, you want to record which thread created or “owns” a particular heap-allocated structure. Storing the thread ID alongside the data makes this possible. Another thread can later check which thread originally created it.
2. pthread_self() — Getting Your Own Thread ID
#include <pthread.h>
pthread_t pthread_self(void);
/* Returns: the thread ID of the calling thread */
pthread_self() returns the thread ID of the thread that calls it. This is how a thread discovers its own identity. It always succeeds — there is no error return.
Important: The standard does not guarantee that a new thread’s ID is written into the pthread_t buffer (the first argument of pthread_create()) before the new thread starts executing. Therefore, if a thread needs to know its own ID at startup, it must call pthread_self() — it cannot rely on reading the buffer that was passed to pthread_create().
/* A thread detaching itself */
pthread_detach(pthread_self());
3. pthread_equal() — Comparing Thread IDs
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
/* Returns: non-zero (true) if t1 and t2 are the same thread, 0 otherwise */
Because pthread_t is an opaque type, you cannot compare two thread IDs using the C == operator — that comparison may not be portable (on some implementations, pthread_t is a struct or pointer). Always use pthread_equal().
/* Check if tid is the same as the current thread */
if (pthread_equal(tid, pthread_self())) {
printf("That's me!\n");
}
4. POSIX Thread IDs vs Linux Kernel Thread IDs
There are actually two different types of thread IDs in Linux, and they are completely separate from each other. This confuses many beginners.
| Property | POSIX Thread ID (pthread_t) | Kernel Thread ID (gettid) |
|---|---|---|
| How to get it | pthread_self() | gettid() (Linux-specific) |
| Type | pthread_t (opaque) | pid_t (integer) |
| Assigned by | Threading library (userspace) | Linux kernel |
| Visible in /proc? | No | Yes (in /proc/PID/task/TID) |
| Portable? | Yes (POSIX) | No (Linux-specific) |
| Unique across processes? | Linux: yes. POSIX: not guaranteed | Yes (system-wide) |
pthread_t is actually a pointer cast to unsigned long. So on Linux you can sometimes print it with printf("%lu", (unsigned long)tid), but this is NOT portable and SUSv3 does not guarantee pthread_t is a scalar.5. Thread ID Reuse
An important subtlety: after a thread has terminated and been joined (or after a detached thread terminates), the OS is allowed to reuse its thread ID for a newly created thread.
This means you should never store a thread ID and try to use it after the thread has terminated and been joined — you might inadvertently operate on a completely different new thread that happens to have the same ID.
pthread_t ID while the thread is known to still be alive (not yet joined/detached-and-finished).6. Code Example: pthread_self() and pthread_equal()
/* compile: gcc -pthread -o thread_id thread_id.c */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
static pthread_t main_tid; /* stored at process start */
static void *worker(void *arg)
{
pthread_t self = pthread_self(); /* get own ID */
/* Check if we are the same thread as main */
if (pthread_equal(self, main_tid))
printf("Worker: I am the main thread (unexpected!)\n");
else
printf("Worker: I am NOT the main thread (correct)\n");
/* On Linux you can print tid as unsigned long for debugging */
printf("Worker: pthread_t value = %lu\n", (unsigned long) self);
return NULL;
}
int main(void)
{
pthread_t tid;
main_tid = pthread_self(); /* save main thread's ID */
printf("Main: pthread_t = %lu\n", (unsigned long) main_tid);
pthread_create(&tid, NULL, worker, NULL);
pthread_join(tid, NULL);
/* Verify equality check works */
if (pthread_equal(tid, main_tid))
printf("Main: tid == main_tid (WRONG)\n");
else
printf("Main: tid != main_tid (CORRECT)\n");
return 0;
}
7. Code Example: Tagging Data with Thread ID
Here is a practical pattern: a work item struct that records which thread created it, so it can later be verified.
/* compile: gcc -pthread -o thread_tag thread_tag.c */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
struct WorkItem {
int data;
pthread_t creator; /* which thread created this item */
};
static void *producer(void *arg)
{
struct WorkItem *item = malloc(sizeof(struct WorkItem));
item->data = 999;
item->creator = pthread_self(); /* tag with our own ID */
printf("Producer: created item with data=%d\n", item->data);
return item; /* pass ownership to joiner */
}
int main(void)
{
pthread_t tid;
void *ret;
struct WorkItem *item;
pthread_create(&tid, NULL, producer, NULL);
pthread_join(tid, &ret);
item = (struct WorkItem *) ret;
/* Verify: the creator should match the thread we spawned */
if (pthread_equal(item->creator, tid))
printf("Main: item was created by the expected thread ✓\n");
else
printf("Main: creator mismatch!\n");
printf("Main: item data = %d\n", item->data);
free(item);
return 0;
}
8. Interview Questions
pthread_self(), which returns the thread ID of the calling thread. It cannot rely on reading the pthread_t buffer filled by pthread_create(), because the new thread may start executing before the buffer is written.pthread_t is an opaque type. Its internal representation is implementation-defined — it could be a struct, pointer, or integer. The == operator may not give correct results for structs. Use pthread_equal() which is portable and correct on all implementations.pthread_t, obtained via pthread_self()) are assigned by the threading library and are opaque. Linux kernel thread IDs are integers assigned by the kernel (obtained via gettid()) and are visible in /proc/PID/task/TID. They are completely independent. Portable applications should not use kernel TIDs.pthread_t is defined as unsigned long, and is actually a pointer cast to that type. However, this is an implementation detail — portable code must treat it as opaque and never assume it is a scalar.How to wait for a thread to finish, retrieve its exit value, and why un-joined threads become “zombie threads”.
