信号的传递和接收是否是线程安全的?

netxuning 2007-09-12 11:46:42
也就是说,本线程发送的信号,会被本线程接收到。

比如如下程序:(但又觉得这个程序并不能说明是这个命题)
#include <stdio.h>
#include <pthread.h>
#include <signal.h>

void
handle ()
{
printf ("hallo thread %d.\n\n", pthread_self ());
}

void *
thr (void *arg)
{
signal (SIGALRM, handle);
printf ("thread %d send a SIGALRM.\n", pthread_self ());
raise (SIGALRM);
}

int
main ()
{
int threads = 5;
pthread_t *pidp = alloca (sizeof (pthread_t) * threads);
int i;

for (i = 0; i < threads; ++i)
{
pthread_create (pidp + i, NULL, thr, NULL);
}

for (i = 0; i < threads; ++i)
{
pthread_join (*(pidp +i), NULL);
}
}

...全文
142 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
hifrog 2007-09-12
  • 打赏
  • 举报
回复
信号的传递和接收不保证线程安全。但会保存现场。
在lz的程序上看,因为没有使用全局变量,因此在用户态看是线程安全的。
dai_weitao 2007-09-12
  • 打赏
  • 举报
回复
用设计避开这种机制上的不足吧.
netxuning 2007-09-12
  • 打赏
  • 举报
回复
to dai_weitao:
可是,这样做意味着每个线程都要sleep(secs)后再运转,违背了用多线程提高效率的初衷.

难道没有一种线程安全的计时机制以及在线程内部进行安全的栈跳转(setjmp, longjmp)机制?
dai_weitao 2007-09-12
  • 打赏
  • 举报
回复
把alarm(secs)改为
sleep(secs);
pthread_kill(tid, SIGALRM);
netxuning 2007-09-12
  • 打赏
  • 举报
回复
谢谢二位.
之所以会问这个问题,是因为我的程序需要如下功能:
用alarm来控制一个函数的运行时间,如果函数运行超过了一定的时间就终止它。
这样做在单线程中是可以,但在多线程中,不知道会将信号发给哪个线程。

如果想使其在多线程环境中正常工作,要如何做呢?
我把代码贴出来:


#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
#include <errno.h>
#include <assert.h>

#define SETJMP(env) sigsetjmp(env, 1)

static sigjmp_buf run_with_timeout_env;

static void
abort_run_with_timeout (int sig)
{
assert (sig == SIGALRM);
siglongjmp (run_with_timeout_env, -1);
}

/* Arrange for SIGALRM to be delivered in TIMEOUT seconds. This uses
setitimer where available, alarm otherwise.

TIMEOUT should be non-zero. If the timeout value is so small that
it would be rounded to zero, it is rounded to the least legal value
instead (1us for setitimer, 1s for alarm). That ensures that
SIGALRM will be delivered in all cases. */

static void
alarm_set (double timeout)
{
/* Use the old alarm() interface. */
int secs = (int) timeout;
if (secs == 0)
/* Round TIMEOUTs smaller than 1 to 1, not to zero. This is
because alarm(0) means "never deliver the alarm", i.e. "wait
forever", which is not what someone who specifies a 0.5s
timeout would expect. */
secs = 1;
alarm (secs);
}

/* Cancel the alarm set with alarm_set. */
static void
alarm_cancel (void)
{
alarm (0);
}

/* Call FUN(ARG), but don 't allow it to run for more than TIMEOUT
seconds. Returns non-zero if the function was interrupted with a
timeout, zero otherwise.

This works by setting up SIGALRM to be delivered in TIMEOUT seconds
using setitimer() or alarm(). The timeout is enforced by
longjumping out of the SIGALRM handler. This has several
advantages compared to the traditional approach of relying on
signals causing system calls to exit with EINTR:

* The callback function is *forcibly* interrupted after the
timeout expires, (almost) regardless of what it was doing and
whether it was in a syscall. For example, a calculation that
takes a long time is interrupted as reliably as an IO
operation.

* It works with both SYSV and BSD signals because it doesn 't
depend on the default setting of SA_RESTART.

* It doesn 't require special handler setup beyond a simple call
to signal(). (It does use sigsetjmp/siglongjmp, but they 're
optional.)

The only downside is that, if FUN allocates internal resources that
are normally freed prior to exit from the functions, they will be
lost in case of timeout. */
/*控制函数指针fun所指向的函数fun(void* arg)运行的时间timeout*/
int
run_with_timeout (double timeout, void (*fun) (void *), void *arg)
{
int saved_errno;

if (timeout == 0)
{
fun (arg);
return 0;
}

signal (SIGALRM, abort_run_with_timeout);
if (SETJMP (run_with_timeout_env) != 0)
{
/* Longjumped out of FUN with a timeout. */
signal (SIGALRM, SIG_IGN);
return 1;
}
alarm_set (timeout); /*set alarm. */
fun (arg);

/* Preserve errno in case alarm() or signal() modifies it. */
saved_errno = errno;
alarm_cancel (); /*don 't timed out, fun() has been over. then destroy the alarm. */
signal (SIGALRM, SIG_IGN);
errno = saved_errno;

return 0;
}

void
myfun (void *arg)
{
int i;
for (i = 0; i < 10; ++i)
{
printf ("sleep %d.\n", i + 1);
sleep (1);
}
}

int
main ()
{
run_with_timeout (5.0, myfun, NULL);
}
dai_weitao 2007-09-12
  • 打赏
  • 举报
回复
信号是发送给一个进程的, 进程内的每个线程都有自己的sigmask, 但是所有线程共享该进程的信号处理.
如果想发送某个信号到某个进程内的某个线程, 不要用raise/kill, 这是进程级的, 应该用pthread_kill
原型: int pthread_kill(pthread_t thread, int signo);

至于楼主所说的线程安全, 由于信号处理是共享的, 所以不能实现.
具体见APUE(2nd) page 333, 12.8 线程和信号.

23,121

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧