首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

C 語言實現 60 秒計時器終止當前函數:sleep、setitimer、alarm 三種方案

     編輯:程式設計疑難解答

需求

在函數執行中設置 60 秒超時,時間到自動中斷該函數並返回/跳出。

方案對比

方案適用場景精度可中斷阻塞 IO可移植性
sleep(60)簡單延遲、無需中斷運算秒級否(僅阻塞當前執行緒)POSIX / Windows (Sleep)
alarm(60) + SIGALRM需中斷長時間運算/阻塞系統呼叫秒級是(信號中斷 read/accept 等)POSIX(Linux/Unix)
setitimer(ITIMER_REAL, ...)需微秒級精度、週期性定時微秒級是POSIX
Windows CreateWaitableTimer + APCWindows 原生高精度、可插隊 APC100ns是(佇列 APC)Windows
跨平台:pthread + 條件變數 / std::future (C++11)現代 C++、需取消點協作式取消毫秒級需代碼配合檢查取消標誌全平台

方案 1:sleep(60) 最簡單(僅延遲,不中斷運算中函數)

#include <unistd.h>  // POSIX
// Windows: #include <windows.h> -> Sleep(60000); // 毫秒

void long_running_task() {
    // ... 大量運算 ...
}

int main() {
    pid_t pid = fork();
    if (pid == 0) {           // 子程序執行任務
        long_running_task();
        exit(0);
    } else {                  // 父程序計時
        sleep(60);
        kill(pid, SIGKILL);   // 強制終止子程序
        waitpid(pid, NULL, 0);
    }
}

⚠️ 缺點: 需 fork 子程序,無法在同一進程中優雅中斷函數;Windows 無 fork。

方案 2:alarm(60) + 信號處理器(POSIX 標準,可中斷阻塞呼叫)

#include <signal.h>
#include <unistd.h>
#include <setjmp.h>

static sigjmp_buf env;
static volatile sig_atomic_t timed_out = 0;

void sigalrm_handler(int sig) {
    timed_out = 1;
    siglongjmp(env, 1);  // 非局部跳轉回 main
}

int long_computation() {
    // 模擬長時間運算
    for (long long i = 0; i < 1e10; ++i) {
        if (timed_out) return -1;  // 協作式檢查(可選)
    }
    return 0;
}

int main() {
    signal(SIGALRM, sigalrm_handler);
    if (sigsetjmp(env, 1) == 0) {
        alarm(60);  // 60 秒後送 SIGALRM
        int ret = long_computation();
        alarm(0);   // 取消鬧鐘
        if (ret == -1) puts("Timed out!");
        else puts("Done.");
    } else {
        puts("Timeout via signal!");
    }
    return 0;
}

⚠️ 注意: 信號處理器中只能呼叫 Async-signal-safe functions(siglongjmp、write、_exit 等),**嚴禁呼叫 printf, malloc, lock 等非安全函數**。

方案 3:setitimer 微秒級精度(同信號機制)

#include <sys/time.h>
struct itimerval timer = {
    .it_value = { .tv_sec = 60, .tv_usec = 0 },  // 首次 60 秒
    .it_interval = { 0, 0 }                       // 不重複
};
setitimer(ITIMER_REAL, &timer, NULL);  // 同樣觸發 SIGALRM

現代最佳實踐:協作式取消(C11 threads / C++11 future)

// C11 threads
#include <threads.h>
#include <stdatomic.h>

atomic_bool cancel_flag = false;

int worker(void *arg) {
    for (int i = 0; i < 1000000; ++i) {
        if (atomic_load(&cancel_flag)) return -1;  // 定期檢查
        // do work
    }
    return 0;
}

int main() {
    thrd_t t;
    thrd_create(&t, worker, NULL);
    thrd_sleep(&(struct timespec){.tv_sec=60}, NULL);
    atomic_store(&cancel_flag, true);
    int res;
    thrd_join(t, &res);
    return res;
}

選擇建議

  • **單純延遲 60 秒再繼續**:sleep(60) / Sleep(60000)。
  • **需中斷阻塞 IO(socket、串口、管道)**:alarm/setitimer + 信號(POSIX);Windows 用 CancelIoEx + 重疊 IO。
  • **需中斷純 CPU 運算**:協作式取消旗標(atomic_bool)+ 定期檢查,最安全、可移植。
  • **C++ 專案**:std::future + std::async + wait_for,或 std::jthread (C++20) + request_stop()。

總結: 沒有銀彈。**信號方案能真正中斷阻塞系統呼叫,但程式設計極難、不可移植、易產生資源洩漏**。現代開發強烈建議**協作式取消**:在長迴圈/阻塞點檢查原子旗標,配合 pthread_cancel (PTHREAD_CANCEL_DEFERRED) 或 C++ std::stop_token,安全又清晰。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved