2016-04-27 74 views
3

我在程序的主要功能中創建了一個POSIX計時器。主程序的每個線程都設置計時器,以便在其到期時,信號處理程序更新一個變量,以喚醒同一進程的下一個線程。POSIX計時器在幾次運行後掛起

計時器在大多數情況下工作正常,但並非總是如此。它有時在其他運行中完成完整的執行,它會掛起。可能的原因是什麼?我的懷疑與信號傳遞有關。

下面是代碼:

#define _GNU_SOURCE 
#define _POSIX_C_SOURCE 199309 
#include <sched.h> 
#include <unistd.h> 
#include <sys/wait.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <pthread.h> 
#include <unistd.h> 
#include <signal.h> 
#include <errno.h> 
#include <semaphore.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <syscall.h> 
#define NUM_THREADS 10 

#define CLOCKID CLOCK_REALTIME 
#define SIG SIGUSR1 
int ret; 
timer_t timerid; 
struct sigevent sev; 
struct itimerspec its; 
long long freq_nanosecs; 
sigset_t mask; 
struct sigaction sa; 

sem_t sem[NUM_THREADS]; 
sem_t mute; 

pthread_t tid[NUM_THREADS]; 
int state = 0; 

static void handler(int sig, siginfo_t *si, void *uc) 
{ 
    ret = sem_post(&sem[(state+1)%NUM_THREADS]); 
     if (ret) 
     { 
      printf("Error in Sem Post\n"); 
     } 
     state++; 
} 

void *threadA(void *data_) 
{ 
    int i = 0, s,n,value; 

    long int loopNum; 
    int turn = (intptr_t)data_; 
    struct timespec tval_result,tval_result2; 

    int sid = syscall(SYS_gettid); 
    FILE *fp; 
    fp=fopen("ipc.out","a");  
    fprintf(fp,"thread_%d %d\n",turn,sid); 
    fclose(fp); 

    int counter=0; 

    while(1) 
    { 
     ret = sem_wait(&sem[turn]); 
     if (ret) 
     { 
      printf("Error in Sem Post\n"); 
     } 
     //printf("Thread # -> %d\n",turn); 

     its.it_value.tv_sec = 0; 
     its.it_value.tv_nsec = 14000; 
     its.it_interval.tv_sec = 0; 
     its.it_interval.tv_nsec = 0; 

     ret = timer_settime(timerid, 0, &its, NULL); 
     if (ret < 0) 
      perror("timer_settime"); 

     // Some heavy work 

    counter++; 

    if(counter==100) 
    break; 
    } 
    printf("finished %d\n",turn); 

} 

int main(int argc, char *argv[]) 
{ 
    int data = 0; 
    int err,i; 

    sa.sa_flags = SA_RESTART; 
    sa.sa_sigaction = handler; 
    sigemptyset(&sa.sa_mask); 
    sigaction(SIG, &sa, NULL); 

    sev.sigev_notify = SIGEV_SIGNAL; 
    sev.sigev_signo = SIG; 
    sev.sigev_value.sival_ptr = &timerid; 
    ret = timer_create(CLOCKID, &sev, &timerid); 
    if (ret < 0) 
     perror("timer_create"); 

    sem_init(&sem[0], 0, 1); 
    for (i = 1; i < NUM_THREADS; ++i) 
     { 
      sem_init(&sem[i], 0, 0); 
     } 

    while(data < NUM_THREADS) 
    { 
     //create our threads 
     err = pthread_create(&tid[data], NULL, threadA, (void *)(intptr_t)data); 
     if(err != 0) 
      printf("\ncan't create thread :[%s]", strerror(err)); 

     data++; 
    } 

    pthread_exit(NULL); 
} 

根據這個,這個程序應該打印

finished 0 
finished 1 
finished 2 
finished 3 
finished 4 
finished 5 
finished 6 
finished 7 
finished 8 
finished 9 

其中一些打印這樣的,但大部分時間,程序掛斷的時間。

回答

1

信號處理程序有競爭條件。只要sem_post被調用,其他線程中的一個可以開始運行,並且其定時器可以在當前信號處理程序完成之前觸發。這將導致信號處理程序在另一個線程中被再次調用。此時state尚未被第一個線程增加,因此第二個信號處理程序調用將最終在錯誤的信號量上調用sem_post。解決這個問題

的一個方法是確保state呼籲sem_post前遞增:

static void handler(int sig, siginfo_t *si, void *uc) 
{ 
    state++; 
    ret = sem_post(&sem[(state)%NUM_THREADS]); 
    if (ret) 
    { 
     printf("Error in Sem Post\n"); 
    } 
} 

注意,該解決方案還包含一個問題。它不能確保printf調用的順序是正確的。

+0

謝謝!這是有道理的,你解釋。我會修改程序以避免任何競爭條件。雖然,你上面告訴我的變化並不是解決這個問題,但它是值得的。 – Scissor

+0

感謝@kaylum的解釋。它解決了我的問題。 – Scissor