2017-07-31 42 views
-1

我在寫一個多線程的linux C應用程序,執行線程需要每隔4小時執行一次(可以從幾秒到幾小時)。目前的實現基於usleep()。但是持續時間較長是非常不準確的。第一次睡眠需要4點20分,第二次5點等等。在linux中解決這個問題的正確方法是什麼?可以使用linux定時器定期調度執行線程以獲得更長的時間間隔嗎?Linux mutithreaded C程序<-> usleep()每小時間隔的調度精度

+1

如果你得到20分鐘的漂移,我會說這是'usleep()'參數的問題,而不是調度問題。你如何計算睡眠間隔? – myaut

+0

類似usleep(x * 60 * 60 * 1000 * 1000),其中x是小時 – t3rmin4tor

+0

'useconds_t'只保證值高達'1.000.000'。我認爲'setitimer()'應該對這個目標更可靠。我會用一個小例子來詳細說明我的答案。 –

回答

1

您可以在SIGALRM處理程序中將setitimer()self-pipe trick結合使用。應定期調度的線程必須在管道的讀取端執行阻塞read()

看到這個玩具的使用示例展示了主意:

#define _POSIX_C_SOURCE 200101L 
#define _BSD_SOURCE 
#include <errno.h> 
#include <pthread.h> 
#include <signal.h> 
#include <stdio.h> 
#include <sys/time.h> 
#include <unistd.h> 

int pipefd[2]; 
const char pipechar = 'x'; 

void *periodicThread(void *arg) 
{ 
    (void)arg; // unused 

    char c; 
    while (read(pipefd[0], &c, 1) > 0) 
    { 
     if (c == 'q') break; 
     puts("scheduled!"); 
    } 
    return 0; 
} 

void alarmHandler(int signum) 
{ 
    (void)signum; // unused 

    write(pipefd[1], &pipechar, 1); 
} 

int main(void) 
{ 
    if (pipe(pipefd) < 0) return 1; 
    int rc = 1; 

    pthread_t thread; 
    if (pthread_create(&thread, 0, periodicThread, 0) != 0) 
     goto cleanup_pipe; 

    struct sigaction sa = { 
     .sa_handler = alarmHandler, 
     .sa_flags = SA_RESTART // aternatively check for EINTR after avery system call 
    }; 

    if (sigaction(SIGALRM, &sa, 0) < 0) goto cleanup_thread; 

    // use (much) larger values here! 
    struct itimerval itv = { 
     .it_interval = { 
      .tv_sec = 5, 
      .tv_usec = 0 
     }, 
     .it_value = { 
      .tv_sec = 5, 
      .tv_usec = 0 
     } 
    }; 

    if (setitimer(ITIMER_REAL, &itv, 0) < 0) goto cleanup_thread; 

    rc = 0; 
    int c; 
    while ((c = getchar()) != EOF) 
    { 
     if (c == 'q') break; 
    } 

    const char pipeendchar = 'q'; 
cleanup_thread: 
    write(pipefd[1], &pipeendchar, 1); 
    pthread_join(thread, 0); 

cleanup_pipe: 
    close(pipefd[0]); 
    close(pipefd[1]); 

    return rc; 
} 

如果你的月經變得很長,即使在struct itimervaltime_t領域,你可以選擇一個更小的週期,只是計數SITGALRM數你接收。

+0

謝謝菲利克斯。我沒有使用超過秒的定時器。 sleep()系列函數的情況也是如此。這是一個特殊的案例,每個人都在談論有關cron的工作,當它變成小時或數天。 – t3rmin4tor

+0

@ t3rmin4tor另請參閱我的編輯。代碼解釋可能比一句話:) –

+0

這是非常有幫助的。欣賞它! – t3rmin4tor