2015-08-29 121 views
0

我正在开发一个小应用程序,我想每1秒调用一个函数。这是我如何实现在使用timer_create的单独pthread中的信号处理程序

Timerspec.it_interval.tv_sec=1; 
Timerspec.it_interval.tv_nsec=0; 
Timerspec.it_value.tv_sec=1; 
Timerspec.it_value.tv_nsec=0; 

timer_t timerId; 

struct sigaction sa; 
sa.sa_handler=&TimerFn; 
sa.sa_flags=0; 
sigemptyset(&sa.sa_mask); 
sigaction(SIGALRM,&sa,NULL); 

timer_create(CLOCK_REALTIME,NULL,&timerId); 
timer_settime(timerId,0,(const itimerspec*)Timerspec,NULL); 

但我想运行在一个独立的并行线程的TimerFn功能(基本上是并行线程功能的定时器)。有人可以告诉如何做到这一点?

+0

对不起,有一个小错字,我纠正了它。 – Harry

+1

看看[POSIX线程和信号](https://stackoverflow.com/questions/2575106/posix-threads-and-signals)。据我所见,回答也有解决您的问题的方法。 – dhke

+0

@dhke我有一个疑问,如果我在一个单独的线程本身创建计时器将信号处理程序在该线程内运行? – Harry

回答

0

如果你能接受每一个计时器滴答一个新线程的创建,您可以使用SIGEV_THREAD

struct sigevent evp; 
memset((void *)&evp, 0, sizeof(evp)); 
evp.sigev_notify = SIGEV_THREAD; 
evp.sigev_notify_function = &sig_alrm_handler; 
evp.sigev_signo = SIGALRM; 
evp.sigev_value.sigval_ptr = (void *)this; 
int ret = timer_create(CLOCK_REALTIME, &evp, &_timerId); 

这将创建一个新的线程,每刻度。

如果您需要处理在一个特定的线程的信号,更多的工作是必需的:

static void * 
sig_threadproc(void *thrarg) 
{ 
    sigset_t sigset; 
    sigemptyset(&sigset); 
    sigaddset(&sigset, SIGALRM); 

    /* endless loop to wait for and handle a signal repeatedly */ 
    for (;;) { 
     int sig; 
     int error; 

     error = sigwait(&sigset, &sig); 
     if (error == 0) { 
      assert(sig == SIGALRM); 
      printf("got SIGALRM\n"); 
     } else { 
      perror("sigwait"); 
     } 
    } 
    return NULL; 
} 

static void 
sig_alrm_handler(int signo) 
{ 
    /** 
    * dummy signal handler, 
    * the signal is actually handled in sig_threadproc() 
    **/ 
} 


int 
main(int argc, char *argv[]) 
{ 
    sigset_t sigset; 
    struct sigaction sa; 
    pthread_t sig_thread; 
    struct itimerspec tspec; 
    timer_t timer_id; 

    /* mask SIGALRM in all threads by default */ 
    sigemptyset(&sigset); 
    sigaddset(&sigset, SIGALRM);   
    sigprocmask(SIG_BLOCK, &sigset, NULL); 

    /* we need a signal handler. 
    * The default is to call abort() and 
    * setting SIG_IGN might cause the signal 
    * to not be delivered at all. 
    **/ 
    memset(&sa, 0, sizeof(sa)); 
    sa.sa_handler = sig_alrm_handler; 
    sigaction(SIGALRM, &sa, NULL); 

    /* create SIGALRM looper thread */ 
    pthread_create(&sig_thread, NULL, sig_threadproc, NULL); 

    /* setup timer */ 
    tspec.it_interval.tv_sec = 1; 
    tspec.it_interval.tv_nsec = 0; 
    tspec.it_value.tv_sec = 1; 
    tspec.it_value.tv_nsec = 0; 

    timer_create(CLOCK_REALTIME, NULL, &timer_id); 
    timer_settime(timer_id, 0, &tspec, NULL); 

    /** 
    * this might return early if usleep() is interrupted (by a signal) 
    * It should not happen, since SIGALRM is blocked on the 
    * main thread 
    **/ 
    usleep(10000000); 

    return 0; 
} 

则可能是只在信号处理器线程选择性解锁SIGARLM,导致它的唯一线索脱身有资格处理该信号,但这可能不是跨系统移植的。

其他版本(包括使用pthread_cond_signal())已在this answer中讨论过。

+0

非常感谢。它真的帮了我很多 – Harry

0

如果你只是想调用一个函数每秒,这里有一个简单的解决方案:

#include <pthread.h> 
#include <unistd.h> 
void TimerFn(void) 
{ 
    ... 
} 
void* timer(void* arg) 
{ 
    for (;;) { 
    TimerFn(); 
    sleep(1); 
    } 
    return NULL; 
} 
... 
    pthread_t timerThread; 
    pthread_create(&timerThread, NULL, timer, NULL); 

是否有某种原因,这是不够的?

+0

,但我想使用timer_create每秒调用一次函数,而不是使用sleep。无论如何感谢您的答案 – Harry

+0

这具有稍微不同的实时属性:它随着'TimerFn()'需要执行的时间延长了滴答的周期,并且这些移位在多个滴答中积累,这可能或可能不适用于该场景。 – dhke

+0

所有便携式计时器都允许四舍五入。此外,通过调用更高分辨率的定时器并适当调整睡眠时间,此解决方案可以轻松修改为匹配1秒时间中心。关键是这个解决方案不那么复杂,因此更容易实现,理解和维护。然而,显然,有一个不明确的标准使得这个解决方案不可接受。 –