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

Linux下无需互斥量的事件等待方案:替代Windows EVENT

Linux下替代Windows EVENT的无锁线程退出方案

在Linux环境中,若不想为独立线程引入互斥锁,以下几种轻量级方案可替代Windows的EVENT对象,实现多线程的退出通知:


1. 使用eventfd(推荐,Linux专属轻量方案)

eventfd是Linux内核提供的专门用于进程/线程间事件通知的文件描述符,无需互斥锁,操作高效且贴合需求。

核心逻辑:

  • 主线程创建eventfd对象,初始计数器设为0
  • 工作线程通过select()/pselect()等待eventfd可读,超时则继续执行任务
  • 主线程向eventfd写入任意值,触发所有等待的线程退出

代码实现:

#include <sys/eventfd.h>
#include <unistd.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <signal.h>

int shutdown_efd;

void* WorkerThread(void* arg) {
    uint64_t buf;
    do {
        // 执行任务逻辑
        printf("Worker thread executing task...\n");

        // 配置超时(5秒示例)
        struct timespec timeout = {5, 0};
        fd_set read_fds;
        FD_ZERO(&read_fds);
        FD_SET(shutdown_efd, &read_fds);

        // 等待事件或超时
        int ret = pselect(shutdown_efd + 1, &read_fds, NULL, NULL, &timeout, NULL);
        if (ret > 0 && FD_ISSET(shutdown_efd, &read_fds)) {
            // 读取eventfd内容重置计数器,然后退出
            read(shutdown_efd, &buf, sizeof(buf));
            printf("Worker thread exiting on shutdown signal...\n");
            return NULL;
        }
    } while (1);
}

void trigger_shutdown(int sig) {
    uint64_t val = 1;
    write(shutdown_efd, &val, sizeof(val));
}

int main() {
    // 创建eventfd,初始值为0
    shutdown_efd = eventfd(0, 0);
    if (shutdown_efd == -1) {
        perror("eventfd creation failed");
        return 1;
    }

    // 注册信号处理(模拟系统关机触发)
    signal(SIGINT, trigger_shutdown);

    // 启动工作线程
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, WorkerThread, NULL);
    pthread_create(&tid2, NULL, WorkerThread, NULL);

    printf("Press Ctrl+C to initiate shutdown...\n");
    pause();

    // 等待线程退出
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    close(shutdown_efd);
    return 0;
}

2. 使用匿名管道(POSIX标准方案)

匿名管道是POSIX标准的IPC机制,通过读写文件描述符实现事件通知,同样无需互斥锁。

核心逻辑:

  • 主线程创建匿名管道,得到读、写两个文件描述符
  • 工作线程在读端等待,管道为空时read()会阻塞,配合select()实现超时等待
  • 主线程向写端写入任意数据,触发所有等待的线程退出

代码实现:

#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <signal.h>
#include <sys/select.h>

int shutdown_pipe[2];

void* WorkerThread(void* arg) {
    char buf;
    do {
        // 执行任务逻辑
        printf("Worker thread executing task...\n");

        // 配置超时(5秒示例)
        struct timespec timeout = {5, 0};
        fd_set read_fds;
        FD_ZERO(&read_fds);
        FD_SET(shutdown_pipe[0], &read_fds);

        // 等待事件或超时
        int ret = pselect(shutdown_pipe[0] + 1, &read_fds, NULL, NULL, &timeout, NULL);
        if (ret > 0 && FD_ISSET(shutdown_pipe[0], &read_fds)) {
            // 读取管道内容,然后退出
            read(shutdown_pipe[0], &buf, 1);
            printf("Worker thread exiting on shutdown signal...\n");
            return NULL;
        }
    } while (1);
}

void trigger_shutdown(int sig) {
    write(shutdown_pipe[1], "x", 1);
}

int main() {
    // 创建匿名管道
    if (pipe(shutdown_pipe) == -1) {
        perror("pipe creation failed");
        return 1;
    }

    // 注册信号处理(模拟系统关机触发)
    signal(SIGINT, trigger_shutdown);

    // 启动工作线程
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, WorkerThread, NULL);
    pthread_create(&tid2, NULL, WorkerThread, NULL);

    printf("Press Ctrl+C to initiate shutdown...\n");
    pause();

    // 等待线程退出
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    close(shutdown_pipe[0]);
    close(shutdown_pipe[1]);
    return 0;
}

3. 使用自定义信号(最简方案)

通过自定义信号(如SIGUSR1)通知线程退出,无需任何同步原语,但需注意信号处理的异步安全性。

核心逻辑:

  • 工作线程注册信号处理函数,设置全局退出标志
  • 主线程向所有工作线程发送自定义信号
  • 工作线程在循环中检查退出标志,或在超时等待时被信号中断后退出

代码实现:

#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

volatile sig_atomic_t shutdown_flag = 0;

void handle_shutdown(int sig) {
    shutdown_flag = 1;
}

void* WorkerThread(void* arg) {
    // 为当前线程注册信号处理
    struct sigaction sa;
    sa.sa_handler = handle_shutdown;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGUSR1, &sa, NULL);

    do {
        // 执行任务逻辑
        printf("Worker thread executing task...\n");

        // 超时等待(5秒),被信号中断则返回-1
        struct timespec timeout = {5, 0};
        nanosleep(&timeout, NULL);

        // 检查退出标志
        if (shutdown_flag) {
            printf("Worker thread exiting on shutdown signal...\n");
            return NULL;
        }
    } while (1);
}

int main() {
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, WorkerThread, NULL);
    pthread_create(&tid2, NULL, WorkerThread, NULL);

    printf("Press Ctrl+C to initiate shutdown...\n");
    pause();

    // 向所有工作线程发送退出信号
    pthread_kill(tid1, SIGUSR1);
    pthread_kill(tid2, SIGUSR1);

    // 等待线程退出
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    return 0;
}

注意事项:

  • 信号处理函数必须仅调用异步安全的函数(如sig_atomic_t赋值)
  • volatile sig_atomic_t确保退出标志在多线程间的可见性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 09:54:54