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

如何通过SIGINT信号立即终止C语言中长期运行的无限循环线程

解决方案

核心原因

你遇到的阻塞问题本质是:getline()内部依赖read()这类慢速系统调用,默认情况下内核会为信号处理函数自动重启被中断的系统调用,所以SIGINT处理完成后getline()会继续阻塞,不会返回。

可行实现方案

方案1:调整信号配置,中断阻塞的系统调用

  1. 替换signal()为sigaction(),关闭SA_RESTART标志,禁止系统调用自动重启,这样SIGINT触发后getline()会立刻返回-1,errno被设置为EINTR。
  2. 多线程环境下建议统一让主线程处理信号,子线程屏蔽SIGINT,避免信号随机递送到子线程导致逻辑混乱。
  3. 调整getline()的错误判断逻辑,返回-1时检查是否为信号中断,如果是则判断全局退出标记。

修改后的核心代码示例:

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

volatile sig_atomic_t exitRequested = 0;

// 信号处理函数不要调用printf,它不是异步信号安全的
void sigint_handler(int sig) {
    const char msg[] = "handler called\n";
    write(STDOUT_FILENO, msg, sizeof(msg)-1);
    exitRequested = 1;
}

void* process_tickers(void* _) {
    // 子线程屏蔽SIGINT,不处理信号
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGINT);
    pthread_sigmask(SIG_BLOCK, &set, NULL);

    while (1){
        printf("ticker is running\n");
        if (exitRequested == 1) {
            break;
        }
        // 这里假设dec_time已经正确定义
        if (dec_time == -1) {
            size_t bufsize = 32;
            char *buffer = malloc(32);
            size_t characters;
            characters = getline(&buffer,&bufsize,stdin);
            if (characters > 0) {
                // do something
            } else if (characters == -1 && errno == EINTR) {
                // 被信号中断,检查退出标记
                if (exitRequested == 1) {
                    free(buffer);
                    break;
                }
            }
            free(buffer); // 补充内存释放,避免泄漏
        }
    }
    return NULL;
}

int main() {
    // 主线程先屏蔽SIGINT,保证创建的子线程都继承屏蔽配置
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGINT);
    pthread_sigmask(SIG_BLOCK, &set, NULL);

    pthread_t ticker_thread;
    pthread_create(&ticker_thread, NULL, process_tickers, NULL);
    insertRear(threads, &ticker_thread);

    // 主线程解除SIGINT屏蔽,注册信号处理函数
    struct sigaction sa = {0};
    sa.sa_handler = sigint_handler;
    sa.sa_flags = 0; // 不设置SA_RESTART,禁止系统调用自动重启
    sigemptyset(&sa.sa_mask);
    sigaction(SIGINT, &sa, NULL);
    pthread_sigmask(SIG_UNBLOCK, &set, NULL);

    // 注意:原来的代码pthread_join会阻塞到线程退出才会执行后面的循环
    // 如果main也有业务逻辑要跑,建议把pthread_join放到业务循环结束后
    void * retval = NULL;
    pthread_join(ticker_thread, &retval);
    while (1) {
        // do something;
    }
}

方案2:用IO多路复用避免长期阻塞

如果不想修改信号配置,可以在调用getline()前用poll()监听stdin可读事件,设置短超时,每次超时就检查退出标记:

#include <poll.h>

// process_tickers里的读逻辑替换为:
struct pollfd pfd = {.fd = STDIN_FILENO, .events = POLLIN};
// 超时100ms,最多阻塞100ms就返回检查退出标记
int ret = poll(&pfd, 1, 100);
if (ret > 0 && (pfd.revents & POLLIN)) {
    // 有数据可读,调用getline
    characters = getline(&buffer,&bufsize,stdin);
    if (characters > 0) {
        // do something
    }
} else if (ret < 0 && errno == EINTR) {
    if (exitRequested == 1) {
        free(buffer);
        break;
    }
}
// 超时或者没有数据,回到循环开头检查exitRequested

额外注意事项

  • 你原来的代码中getline()分配的buffer没有释放,会存在内存泄漏,需要补充释放逻辑。
  • 信号处理函数中只能调用异步信号安全的函数,不要使用printf()、malloc()这类非安全函数,避免出现未定义行为。
  • 原来的main函数中pthread_join()会阻塞等待子线程退出,后面的无限循环在线程退出前不会执行,如果你需要main和子线程同时跑业务逻辑,需要调整pthread_join()的位置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 14:54:00