You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++11中实现类似Java的InterruptedException机制?

Great question! Linux's native pthread signal handling is definitely clunky compared to Java's clean interrupt model, where blocked threads get a nice InterruptedException when interrupted. Let's walk through a practical implementation that mimics this behavior—allowing you to interrupt threads waiting on mutexes, semaphores, or joins, with per-thread interrupt state that doesn't interfere with other running threads.

Core Design Idea

The approach centers around two key pieces:

  • A thread-safe per-thread interrupt flag to track whether a thread has been marked for interruption.
  • Wrapped versions of blocking synchronization calls that check this flag before blocking, and handle wake-ups from a dedicated interrupt signal to re-check the flag.

Unlike raw pthread signals (which are global and hard to scope), this model keeps interrupt state isolated to individual threads, just like Java's Thread.interrupt().


1. Thread Interrupt State Management

First, we need a way to track interrupt state per thread, and a safe way for other threads to trigger an interrupt. We'll use a thread-safe hash table to map pthread_t IDs to their interrupt flags, plus a dedicated signal (SIGUSR1) to wake blocked threads.

Setup Code

#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <stdbool.h>

// Thread-safe hash table (simple mutex-protected structure for example)
typedef struct {
    pthread_mutex_t mutex;
    pthread_t *tids;
    bool *flags;
    size_t count;
    size_t capacity;
} InterruptTable;

static InterruptTable g_interrupt_table;
static pthread_key_t t_interrupt_key; // For current thread to access its own flag

// Initialize the interrupt system
void interrupt_init(void) {
    pthread_mutex_init(&g_interrupt_table.mutex, NULL);
    g_interrupt_table.tids = malloc(8 * sizeof(pthread_t));
    g_interrupt_table.flags = malloc(8 * sizeof(bool));
    g_interrupt_table.capacity = 8;
    g_interrupt_table.count = 0;
    
    pthread_key_create(&t_interrupt_key, NULL);
}

// Cleanup the interrupt system
void interrupt_cleanup(void) {
    pthread_mutex_lock(&g_interrupt_table.mutex);
    free(g_interrupt_table.tids);
    free(g_interrupt_table.flags);
    pthread_mutex_unlock(&g_interrupt_table.mutex);
    pthread_mutex_destroy(&g_interrupt_table.mutex);
    pthread_key_delete(t_interrupt_key);
}

// Register the current thread for interrupt support
void thread_register_for_interrupt(void) {
    pthread_mutex_lock(&g_interrupt_table.mutex);
    
    // Check if already registered
    for (size_t i = 0; i < g_interrupt_table.count; i++) {
        if (pthread_equal(g_interrupt_table.tids[i], pthread_self())) {
            pthread_setspecific(t_interrupt_key, &g_interrupt_table.flags[i]);
            pthread_mutex_unlock(&g_interrupt_table.mutex);
            return;
        }
    }
    
    // Resize table if needed
    if (g_interrupt_table.count == g_interrupt_table.capacity) {
        g_interrupt_table.capacity *= 2;
        g_interrupt_table.tids = realloc(g_interrupt_table.tids, g_interrupt_table.capacity * sizeof(pthread_t));
        g_interrupt_table.flags = realloc(g_interrupt_table.flags, g_interrupt_table.capacity * sizeof(bool));
    }
    
    // Add current thread to the table
    g_interrupt_table.tids[g_interrupt_table.count] = pthread_self();
    g_interrupt_table.flags[g_interrupt_table.count] = false;
    pthread_setspecific(t_interrupt_key, &g_interrupt_table.flags[g_interrupt_table.count]);
    g_interrupt_table.count++;
    
    pthread_mutex_unlock(&g_interrupt_table.mutex);
}

// Interrupt a target thread
void thread_interrupt(pthread_t tid) {
    pthread_mutex_lock(&g_interrupt_table.mutex);
    
    for (size_t i = 0; i < g_interrupt_table.count; i++) {
        if (pthread_equal(g_interrupt_table.tids[i], tid)) {
            g_interrupt_table.flags[i] = true;
            pthread_kill(tid, SIGUSR1); // Wake up any blocked system calls
            break;
        }
    }
    
    pthread_mutex_unlock(&g_interrupt_table.mutex);
}

// Check if current thread is interrupted
bool thread_is_interrupted(void) {
    bool *flag = pthread_getspecific(t_interrupt_key);
    return flag ? *flag : false;
}

// Reset interrupt state for current thread
void thread_reset_interrupt(void) {
    bool *flag = pthread_getspecific(t_interrupt_key);
    if (flag) *flag = false;
}

Signal Handler

We just need a minimal handler to wake blocked calls—no heavy logic here:

void interrupt_signal_handler(int sig) {
    // Do nothing except wake up blocked system calls like sigsuspend()
}

2. Wrapping Blocking Synchronization Calls

Now we wrap all the blocking operations you care about (mutex lock, semaphore wait, pthread join) to check for interrupts before and during blocking.

Interruptible Mutex Lock

int pthread_mutex_lock_interruptible(pthread_mutex_t *mutex) {
    while (1) {
        // Check if interrupted before attempting to lock
        if (thread_is_interrupted()) {
            return -EINTR; // Custom error to signal interrupt
        }
        
        // Try to lock without blocking
        int ret = pthread_mutex_trylock(mutex);
        if (ret == 0) {
            return 0; // Lock acquired successfully
        } else if (ret != EBUSY) {
            return ret; // Actual error, not just busy
        }
        
        // Prepare to wait for interrupt or mutex availability
        struct sigaction sa, old_sa;
        sa.sa_handler = interrupt_signal_handler;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        sigaction(SIGUSR1, &sa, &old_sa);
        
        // Wait for SIGUSR1 to wake us up
        sigset_t wait_mask;
        sigemptyset(&wait_mask);
        sigsuspend(&wait_mask);
        
        // Restore original signal handler
        sigaction(SIGUSR1, &old_sa, NULL);
    }
}

Interruptible Semaphore Wait

int sem_wait_interruptible(sem_t *sem) {
    while (1) {
        if (thread_is_interrupted()) {
            return -EINTR;
        }
        
        int ret = sem_trywait(sem);
        if (ret == 0) {
            return 0;
        } else if (ret != EAGAIN) {
            return ret;
        }
        
        // Same signal handling as mutex lock
        struct sigaction sa, old_sa;
        sa.sa_handler = interrupt_signal_handler;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        sigaction(SIGUSR1, &sa, &old_sa);
        
        sigset_t wait_mask;
        sigemptyset(&wait_mask);
        sigsuspend(&wait_mask);
        
        sigaction(SIGUSR1, &old_sa, NULL);
    }
}

Interruptible Thread Join

For joins, we use pthread_timedjoin_np to periodically check for interrupts:

int pthread_join_interruptible(pthread_t thread, void **retval) {
    struct timespec ts;
    while (1) {
        if (thread_is_interrupted()) {
            return -EINTR;
        }
        
        // Wait for 1 second, then re-check interrupt state
        clock_gettime(CLOCK_REALTIME, &ts);
        ts.tv_sec += 1;
        
        int ret = pthread_timedjoin_np(thread, retval, &ts);
        if (ret == 0) {
            return 0; // Thread finished successfully
        } else if (ret != ETIMEDOUT) {
            return ret; // Actual error
        }
    }
}

3. Using the Interrupt System

In your threads, you just need to:

  1. Register the thread for interrupts with thread_register_for_interrupt() at startup.
  2. Use the wrapped interruptible functions instead of raw pthread/sem calls.
  3. Check for -EINTR return codes to handle interrupts (in C++ you could throw a custom InterruptedException here).

Example Thread Code

void *worker_thread(void *arg) {
    thread_register_for_interrupt();
    
    pthread_mutex_t *mutex = (pthread_mutex_t*)arg;
    
    while (1) {
        int ret = pthread_mutex_lock_interruptible(mutex);
        if (ret == -EINTR) {
            // Handle interrupt: clean up and exit
            printf("Thread %lu was interrupted, exiting\n", (unsigned long)pthread_self());
            return NULL;
        } else if (ret != 0) {
            // Handle actual mutex error
            perror("pthread_mutex_lock_interruptible");
            return NULL;
        }
        
        // Do work with the mutex held
        printf("Thread %lu acquired mutex\n", (unsigned long)pthread_self());
        sleep(1);
        
        pthread_mutex_unlock(mutex);
    }
}

Triggering an Interrupt

From another thread, just call thread_interrupt(target_tid):

pthread_t worker;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_create(&worker, NULL, worker_thread, &mutex);

// Later, interrupt the worker
sleep(3);
thread_interrupt(worker);
pthread_join(worker, NULL);

Key Notes

  • Thread Safety: The interrupt table is protected by a mutex, so multiple threads can trigger interrupts safely.
  • No Cross-Thread Interference: Each thread's interrupt flag is independent—interrupting one thread doesn't affect others.
  • Signal Safety: The signal handler is minimal (does nothing), so it's safe to use with system calls.
  • Cleanup: Don't forget to call interrupt_cleanup() when your program exits to free resources.

内容的提问来源于stack exchange,提问作者peterk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:57:26