Detaching a Thread

Detaching a Thread
Part 7 of 9  |  pthread_detach() · Joinable vs Detached · Auto-cleanup
Level
Beginner
Function
pthread_detach()
Book
TLPI – Ch 29.7

Not every thread needs to have its return value collected. When you create a background worker that just does its job and exits — and you don’t need to know when it finishes or what it returned — calling pthread_join() is extra overhead. The solution is to detach the thread. A detached thread automatically releases all its resources when it terminates, with no join required.

Key Terms

pthread_detach() Detached Thread Joinable Thread pthread_attr_setdetachstate() PTHREAD_CREATE_DETACHED Auto-cleanup Fire and Forget

1. Two States: Joinable vs Detached

By default, every thread created by pthread_create() is joinable. A joinable thread must be joined after it terminates — otherwise it becomes a zombie thread.

A thread can also be in the detached state. When a detached thread terminates, the system automatically frees all its resources. No other thread needs to (or can) call pthread_join() for it.

JOINABLE Thread (default)
  • Created by default
  • Resources held until joined
  • Return value can be retrieved
  • Must call pthread_join()
  • Becomes zombie if not joined
DETACHED Thread
  • Created via detach call or attribute
  • Resources freed automatically on exit
  • Return value is discarded
  • Cannot be joined (error if attempted)
  • Cannot be made joinable again

2. pthread_detach() — Detach After Creation

#include <pthread.h>

int pthread_detach(pthread_t thread);

/* Returns: 0 on success, positive error number on failure */

Call this after creating a thread when you decide you don’t need to join it. You can also call it from inside the thread itself to make it self-detaching:

/* Inside the thread's start function */
pthread_detach(pthread_self());
Important rules:

  • Once detached, a thread cannot be made joinable again
  • Calling pthread_join() on a detached thread returns an error (EINVAL)
  • Detaching a thread does not affect what happens when exit() is called — all threads are still killed
  • pthread_detach() only controls cleanup after normal thread termination

3. Creating a Detached Thread at Birth (Using Attributes)

Instead of creating a joinable thread and then calling pthread_detach(), you can set the thread attribute to detached before creation. This avoids a race condition where the thread might finish before you call pthread_detach().

The steps are:

  1. Declare a pthread_attr_t variable
  2. Initialise it with pthread_attr_init()
  3. Set detached state with pthread_attr_setdetachstate()
  4. Pass the attribute to pthread_create()
  5. Destroy the attribute with pthread_attr_destroy()
pthread_t      thr;
pthread_attr_t attr;
int            s;

s = pthread_attr_init(&attr);           /* (2) initialise with defaults */
if (s != 0) { /* handle error */ }

s = pthread_attr_setdetachstate(&attr,  /* (3) set detached */
                                PTHREAD_CREATE_DETACHED);
if (s != 0) { /* handle error */ }

s = pthread_create(&thr, &attr, start_fn, arg);  /* (4) create */
if (s != 0) { /* handle error */ }

s = pthread_attr_destroy(&attr);        /* (5) clean up attr */
if (s != 0) { /* handle error */ }

4. Code Example: Self-Detaching Background Logger

A common use-case: a logging thread that runs in the background and you don’t need to join it. It detaches itself on startup.

/* compile: gcc -pthread -o detach_logger detach_logger.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

static void *logger_thread(void *arg)
{
    /* Detach self — no one needs to join us */
    pthread_detach(pthread_self());

    int count = (int)(intptr_t) arg;

    for (int i = 0; i < count; i++) {
        printf("[LOG] Message %d from background logger\n", i + 1);
        sleep(1);
    }

    printf("[LOG] Logger done. Exiting.\n");
    return NULL;   /* resources freed automatically */
}

int main(void)
{
    pthread_t tid;
    int s;

    /* Create logger thread — we will NOT call pthread_join() */
    s = pthread_create(&tid, NULL, logger_thread, (void *)(intptr_t) 3);
    if (s != 0) { fprintf(stderr, "create failed\n"); exit(1); }

    printf("Main: logger started (detached), continuing other work...\n");

    /* Main does its own work */
    for (int i = 0; i < 3; i++) {
        printf("Main: working... step %d\n", i + 1);
        sleep(1);
    }

    printf("Main: done. Calling pthread_exit so logger can finish.\n");

    /*
     * Use pthread_exit() so the logger thread can finish.
     * If we called exit() or return here, the logger would be killed.
     */
    pthread_exit(NULL);
}

5. Code Example: Detached Thread via Attributes

This is the TLPI Listing 29-2 pattern — creating a detached thread using pthread_attr_t at creation time.

/* compile: gcc -pthread -o detach_attr detach_attr.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

static void *background_worker(void *arg)
{
    int id = (int)(intptr_t) arg;
    printf("Background worker %d: running\n", id);
    sleep(2);
    printf("Background worker %d: done (will self-cleanup)\n", id);
    return NULL;
}

int main(void)
{
    pthread_t      thr;
    pthread_attr_t attr;
    int            s;

    /* Step 1: Initialise attribute object */
    s = pthread_attr_init(&attr);
    if (s != 0) { fprintf(stderr, "attr_init: %s\n", strerror(s)); exit(1); }

    /* Step 2: Set detached state */
    s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    if (s != 0) { fprintf(stderr, "setdetachstate: %s\n", strerror(s)); exit(1); }

    /* Step 3: Create thread with the attribute */
    s = pthread_create(&thr, &attr, background_worker, (void *)(intptr_t) 1);
    if (s != 0) { fprintf(stderr, "create: %s\n", strerror(s)); exit(1); }

    /* Step 4: Destroy the attribute — no longer needed */
    s = pthread_attr_destroy(&attr);
    if (s != 0) { fprintf(stderr, "attr_destroy: %s\n", strerror(s)); exit(1); }

    printf("Main: detached thread created, no need to join.\n");

    /* Try to join a detached thread — should fail */
    s = pthread_join(thr, NULL);
    if (s != 0)
        printf("Main: pthread_join failed as expected: %s\n", strerror(s));

    /* Give background thread time to print */
    pthread_exit(NULL);
    return 0;
}

You should see: pthread_join failed as expected: Invalid argument (EINVAL) because you cannot join a detached thread.

6. Interview Questions

Q1. What is the difference between a joinable and a detached thread?
A joinable thread (the default) must be joined after termination via pthread_join(); its resources are only freed at that point and its return value is available. A detached thread automatically frees its resources when it terminates; no join is needed or possible.
Q2. What happens if you call pthread_join() on a detached thread?
It returns an error — specifically EINVAL (invalid argument). Detached threads cannot be joined.
Q3. Can a detached thread be made joinable again?
No. The detach state is a one-way transition. Once a thread is detached, it cannot be converted back to joinable.
Q4. Does detaching a thread protect it from exit()?
No. If any thread calls exit() or if the main thread returns from main(), all threads are immediately terminated regardless of whether they are joinable or detached. Detaching only controls what happens after a thread terminates normally — it does not protect from process-wide termination.
Q5. What is the advantage of creating a detached thread at creation time vs calling pthread_detach() afterwards?
Creating a detached thread at creation time (using PTHREAD_CREATE_DETACHED attribute) avoids a race condition: the thread might terminate before you call pthread_detach(), potentially becoming a zombie briefly. Setting the attribute at creation time ensures the thread is detached from the very first moment.

Next: Part 8 — Thread Attributes

Explore the full pthread_attr_t object — controlling stack size, scheduling policy, and more at thread creation time.

Leave a Reply

Your email address will not be published. Required fields are marked *