linux alarm 能在线程中用吗

linux alarm 能在线程中用吗,第1张

不管是在进程还是线程,很多时候我们都会使用一些定时器之类的功能,这里就定时器在多线程的使用说一下。首先在linux编程中定时器函数有alarm()和setitimer(),alarm()可以提供一个基于秒的定时功能,而setitimer可以提供一个基于微妙的定时功能。

alarm()原型:

#include <unistd.h>

unsigned int alarm(unsigned int seconds)

这个函数在使用上很简单,第一次调用这个函数的时候是设置定时器的初值,下一次调用是重新设置这个值,并会返回上一次定时的剩余时间。

setitimer()原型:

#include <sys/time.h>

int setitimer(int which, const struct itimerval *value,struct itimerval *ovalue)

这个函数使用起来稍微有点说法,首先是第一个参数which的值,这个参数设置timer的计时策略,which有三种状态分别是:

ITIMER_REAL:使用系统时间来计数,时间为0时发出SIGALRM信号,这种定时能够得到一个精准的定时,当然这个定时是相对的,因为到了微秒级别我们的处理器本身就不够精确。

ITIMER_VIRTUAL:使用进程时间也就是进程分配到的时间片的时间来计数,时间为0是发出SIGVTALRM信号,这种定时显然不够准确,因为系统给进程分配时间片不由我们控制。

ITIMER_PROF:上面两种情况都能够触发

第二个参数参数value涉及到两个结构体:

struct itimerval {

struct timeval it_interval /* next value */

struct timeval it_value /* current value */

}

struct timeval {

long tv_sec /* seconds */

long tv_usec /* microseconds */

}

在结构体itimerval中it_value是定时器当前的值,it_interval是当it_value的为0后重新填充的值。而timeval结构体中的两个变量就简单了一个是秒一个是微秒。

上面是这两个定时函数的说明,这个函数使用本不是很难,可以说是很简单,但是碰到具体的应用的时候可能就遇到问题了,在多进程编程中使用一般不会碰到什么问题,这里说的这些问题主要体现在多线程编程中。比如下面这个程序:

#include <pthread.h>

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/time.h>

void sig_handler(int signo)

{

alarm(2)

printf("alarm signal\n")

}

void *pthread_func()

{

alarm(2)

while(1)

{

pause()

}

}

int main(int argc, char **argv)

{

pthread_t tid

int retval

signal(SIGALRM, sig_handler)

if((retval = pthread_create(&tid, NULL, pthread_func, NULL)) <0)

{

perror("pthread_create")

exit(-1)

}

while(1)

{

printf("main thread\n")

sleep(10)

}

return 0

}

这个程序的理想结果是:

main thread

alarm signal

alarm signal

alarm signal

alarm signal

alarm signal

main thread

可事实上并不是这样的,它的结果是:

main pthread

alarm signal

main pthread

alarm signal

main pthread

为什么会出现这种情况呢?是因为发送给工作线程的信号中断的主线程的sleep,并且这个中情况只影响主线程而不会影响到其他的工作线程。我们怎么才能解决这种问题呢,最简单的方法是修改这个程序,修改这个线程主线程使用alarm,工作线程使用sleep。这样就能够达到我们的要求,但是有时候有不能简单的这样 *** 作。所以我们就需要进一步的修改我们的程序。在这里我第一个想到的是使用signal(SIGALRM, SIG_IGN),可是这个是设置整个进程对这个信号的响应方式,经过测试也确实不能完成我期望的功能,那么怎么办呢?有这样一个函数pthread_sigmask,线程中的信号屏蔽,函数的原型及相关函数为:

#include <signal.h>

int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset)

函数中第一个参数how有三个值SIG_BLOCK、SIG_SETMASK和SIG_UNBLOCK这里我们是用第二个值SIG_SETMASK

int sigemptyset(sigset_t *set) /*清除信号集合set*/

int sigaddset(sigset_t *set, int signum)/*添加信号signum到信号集set中*/

然后我们改造我们的程序为:

#include <pthread.h>

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/time.h>

void sig_handler(int signo)

{

alarm(2)

printf("alarm signal\n")

}

void *pthread_func()

{

alarm(2)

while(1)

{

pause()

}

}

int main(int argc, char **argv)

{

pthread_t tid, tid_1

int retval

signal(SIGALRM, sig_handler)

if((retval = pthread_create(&tid, NULL, pthread_func, NULL)) <0)

{

perror("pthread_create")

exit(-1)

}

sigset_t sigset

sigemptyset(&sigset)

sigaddset(&sigset, SIGALRM)

pthread_sigmask(SIG_SETMASK,&sigset,NULL)

while(1)

{

printf("main pthread\n")

sleep(10)

}

return 0

}

这个时候我们就能够看到我们想要的结果了。

这里再附一个setitimer的使用范例:

#include <pthread.h>

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/time.h>

struct itimerval timerval

void sig_handler(int signo)

{

printf("alarm signal\n")

}

void *pthread_func()

{

setitimer(ITIMER_REAL, &timerval, NULL)

while(1)

{

pause()

}

}

int main(int argc, char **argv)

{

pthread_t tid

int retval

timerval.it_interval.tv_sec = 2

timerval.it_interval.tv_usec = 0

timerval.it_value.tv_sec = 2

timerval.it_value.tv_usec = 0

signal(SIGALRM, sig_handler)

if((retval = pthread_create(&tid, NULL, pthread_func, NULL)) <0)

{

perror("pthread_create")

exit(-1)

}

sigset_t sigset

sigemptyset(&sigset)

sigaddset(&sigset, SIGALRM)

pthread_sigmask(SIG_SETMASK,&sigset,NULL)

while(1)

{

printf("main thread\n")

sleep(5)

}

return 0

}

pause函数使调用进程挂起直至捕捉到一个信号。

#include

int

pause(void)

返回:-1,errno设置为EINTR

只有执行了一个信号处理程序并从其返回时,pause才返回。

/*************************************

使用alarm函数定时,然后通过pause()等待alarm函数的信号。

1:使用alarm函数的时候要注意alarm函数的覆盖性,即在一个进程中采用一次alarm函数则该进程之前的alarm函数将失效。

2:pause函数为将进程挂起,然后等待信号。

3:因为alarm函数在定时器到点的时候产生的信号默认为让该进程退出。因此本代码运行5秒然后直接退出,终端不会输出printf中的语句。

tips:挂起和阻塞的区别为,挂起是进程主动行为,阻塞是进程的被动行为。

*************************************/

#include

#include

#include

int

main(int

argc,char

*argv[])

{

int

ret

ret=alarm(5)

pause()

printf("will

this

be

printed?\n")

}

#include

#include

#include

int

main()

{

int

ret

ret=alarm(5)

pause()

printf("I

have

been

waken

up.\n",ret)

}

默认情况下,收到信号后,被阻塞的系统调用(read) 会直接返回-1,同时 errno 被置成 EINTR 。这个error对应的错误信息应该类似于 " Interrupted system call"

但是很不幸,在linux上,你的代码是看不到这个现象的,因为你使用 signal() 来注册信号处理,它会把 SA_RESTART 打开,这个会导致read系统调用不返回,而是重新开始执行read *** 作 (这些重新执行的动作都是发生在内核,用户代码是看不到的),所以你的代码在alarm信号发生后,仍然会继续read。

如果要看到read被中断的情况,你必须使用 sigaction 这个借口来注册信号处理,

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)

把第二个参数 act 的成员 sa_flags 置零 (确保没有打开 SA_RESTART),你就能看到read被中断的现象了。

建议你阅读一下 Advanced Programming in the Unix Environment 这本书中关于信号相关的内容。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/6250884.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-03-19
下一篇 2023-03-19

发表评论

登录后才能评论

评论列表(0条)

保存