线程无限期地等待定时器信号

时间:2018-02-23 19:02:56

标签: c multithreading timer pthreads

我在为多线程程序创建和实现计时器方面遇到了麻烦。我创建3个线程,他们应该分别等待1,2和4秒。然而,所有三个线程永远不会停止等待,程序只是无限期地坐在那里。

我需要看看我的两个功能:

CreateAndArmTimer():

-I'我不确定我是否正确使用sigemptyset和sigaddset。我应该"在timer_signal"中创建与所选signal_number相对应的信号掩码。我基本上查看了pthread_sigmask的手册页并复制了我在那里找到的内容。

WaitFortimer():

- 这个功能是导致我的程序无法完成的原因。我的线程正常运行,直到这一点,一旦他们调用这个函数,他们就会陷入其中而永远不会退出。

这两个函数都位于我的代码的底部。我很感激任何帮助!我无法为我的生活做到这一点。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/time.h>
#include <time.h>
#include <string.h>
#include <signal.h>



int threadNumber = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

#define NUM_THREADS    3

//used to store the information of each thread
typedef struct{
   pthread_t  threadID;
   int num;
   int policy;
   struct sched_param param;
   long startTime;
   long endTime;


   int signal_number;
   int missed_signal_count;
   int timer_Period;

   sigset_t timer_signal;

   timer_t timer_Id;



}ThreadInfo;

ThreadInfo myThreadInfo[NUM_THREADS];

void *ThreadRunner(void *vargp);
void CreateAndArmTimer(int unsigned period, ThreadInfo* threadInfo);
void WaitForTimer(ThreadInfo* threadInfo);
int sigwait(const sigset_t* set, int* sig);
int timer_create(clockid_t clockid, struct sigevent* sevp, timer_t* timerid);




//main function
int main(void){

   sigset_t alarm_sig;
   sigemptyset(&alarm_sig);
   for(int i = SIGRTMIN; i <= SIGRTMAX; i++)
   sigaddset(&alarm_sig, i);
   pthread_sigmask(SIG_BLOCK, &alarm_sig, NULL);  //*****apply the blocking*****


   printf("\nrunning...\n");

   int fifoPri = 60;

   //create the 3 fifo threads
   for(int i=0; i<NUM_THREADS; i++){

         myThreadInfo[i].policy = SCHED_FIFO; 
         myThreadInfo[i].param.sched_priority = fifoPri++; 

      pthread_create(&myThreadInfo[i].threadID, NULL, ThreadRunner, &myThreadInfo[i]);

   }


   printf("\n\n");
   sleep(1);

   //tell all the threads to unlock
   pthread_cond_broadcast(&cond);





   //join each thread
   for(int g = 0; g < NUM_THREADS; g++){

      pthread_join(myThreadInfo[g].threadID, NULL);

   }


   return 0;
}





//the function that runs the threads
void *ThreadRunner(void *vargp){          

   struct tm *ts;                        
   struct timeval  tv;                    
   size_t last;                           
   time_t timestamp = time(NULL);
   threadNumber++;            
   ThreadInfo* currentThread;         
   currentThread = (ThreadInfo*)vargp;
   currentThread->num = threadNumber;
   if(currentThread->num == 1){
      currentThread->timer_Period = 1000000;
   }
   else if(currentThread->num == 2){
      currentThread->timer_Period = 2000000;
   }
   else{
      currentThread->timer_Period = 4000000;
   }




        //lock the thread until it's ready to be unlocked 
        pthread_mutex_lock(&mutex);

        pthread_cond_wait(&cond, &mutex);       

        //unlocking for all other threads
        pthread_mutex_unlock(&mutex); 



   if(pthread_setschedparam(pthread_self(), currentThread->policy,(const struct sched_param *) &(currentThread->param))){
      perror("pthread_setschedparam failed");
      pthread_exit(NULL);
   }

   if(pthread_getschedparam(pthread_self(), &currentThread->policy,(struct sched_param *) &currentThread->param)){
      perror("pthread_getschedparam failed");
      pthread_exit(NULL);
   }


   //create and arm the timer
   printf("thread#[%d] waiting for %d seconds\n", currentThread->num, (currentThread->timer_Period/1000000));
   CreateAndArmTimer(currentThread->timer_Period, currentThread);



   //set the start time of the timer
   gettimeofday(&tv, NULL);
   long startTime = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000;
   currentThread->startTime = startTime;


   //Wait for the timer
   WaitForTimer(currentThread);


   //set the end time of the timer
   gettimeofday(&tv, NULL);
   long endTime = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000;
   currentThread->endTime = endTime;

   //do the printing
   printf("\nThread[%d]  Timer Delta[%lu]us           Jitter[]us\n", currentThread->num, endTime-startTime);



   pthread_exit(NULL);
}




//used to create and arm a new timer
void CreateAndArmTimer(int unsigned period, ThreadInfo* threadInfo){

   //Create a static int variable to keep track of the next available signal number
   pthread_mutex_lock(&mutex);  
   static int nextSignalNumber = 0;  

   if(nextSignalNumber == 0){
      nextSignalNumber = SIGRTMIN;
   }
   else{
      nextSignalNumber += 1;
   }

   pthread_mutex_unlock(&mutex); 


   threadInfo->signal_number = nextSignalNumber;


   //Create the signal mask corresponding to the chosen signal_number in "timer_signal"
   //Use "sigemptyset" and "sigaddset" for this
   sigemptyset(&threadInfo->timer_signal);
   sigaddset(&threadInfo->timer_signal, SIGQUIT);
   sigaddset(&threadInfo->timer_signal, SIGUSR1);


   //Use timer_Create to create a timer
   struct sigevent mySignalEvent;
   mySignalEvent.sigev_notify = SIGEV_SIGNAL;
   mySignalEvent.sigev_signo = threadInfo->signal_number;
   mySignalEvent.sigev_value.sival_ptr = (void*)&(threadInfo->timer_Id);
   int ret = timer_create(CLOCK_MONOTONIC, &mySignalEvent, &threadInfo->timer_Id);
   if(ret != 0){
      printf("error during timer_create for thread#[%d]\n", threadInfo->num);
   }


   //Arm timer
   struct itimerspec timerSpec;
   int seconds = period/1000000;
   long nanoseconds = (period - (seconds * 1000000)) * 1000;
   timerSpec.it_interval.tv_sec = seconds;
   timerSpec.it_interval.tv_nsec = nanoseconds;
   timerSpec.it_value.tv_sec = seconds;
   timerSpec.it_value.tv_nsec = nanoseconds;
   int ret2 = timer_settime(threadInfo->timer_Id, 0, &timerSpec, NULL);
   if(ret2 != 0){
      printf("error with timer_settime!\n");
   }

}



//used to make a thread wait for a timer
void WaitForTimer(ThreadInfo* threadInfo){


   pthread_sigmask(SIG_UNBLOCK, &threadInfo->timer_signal, NULL); //*****unblock the signal*****


   //Use sigwait function to wait on the "timer_signal"
   int wait = sigwait(&threadInfo->timer_signal, &threadInfo->signal_number);
   if(wait != 0){
      printf("error with sigwait!\n");
   }

   //update missed_signal_count by calling "timer_getoverrun"
   threadInfo->missed_signal_count = timer_getoverrun(threadInfo->timer_Id);

}

当我运行它时,输出为:

...运行

线程#[l]等待1秒

线程#[2]等待2秒

线程#[3]等待4秒

1 个答案:

答案 0 :(得分:0)

首先,您应该使用pthread_sigmask(2)而不是sigprocmask(2)。除了您的注释(指令,如果这是作业?)状态要使用之外,前者在多线程程序中明确指定为POSIX标准的一部分,而后者则不是。我不认为这在Linux上很重要,但它可能是一种很好的做法。

第二个,更重要的是,你并没有真正正确使用这些信号。首先,您通过主函数中sigprocmask(2)的调用来阻止每个信号,但之后永远不会改变它。在CreateAndArmTimer()函数中,您实际上从未指定应阻止之外的所有信号threadInfo->signal_number。您改为将SIGQUITSIGUSR1添加到sigset,但之后不会对该集合执行任何操作。你的意思是在这里打电话给pthread_sigmask(2)吗?如果是这样,您应该确保在此之前添加threadInfo->signal_number

关于&#34;听&#34;另外,您实际上 unblock WaitForTimer()函数(或其他任何地方)中的任何信号。即使您之前已正确阻止它们,如果您在调用sigwait(2)之前未取消阻止它们,它们也永远不会被传递到您的主题。因此,计时器正在生成所请求的信号,但它们只是位于您的过程的信号队列中。你必须在某处调用pthread_sigmask(SIG_UNBLOCK, ...),以便实际交付。

简而言之:

  • 致电pthread_sigmask(2)而非sigprocmask(2)
  • 阻止除线程中所选threadInfo->signal_number之外的所有信号。
  • 在致电sigwait(2)之前取消阻止这些信号。