2016-04-27 2 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

Смежные вопросы