可重入函数和线程安全的对比

线程安全

一般说来,一个函数被称为线程安全的,当且仅当被多个并发线程反复调用时,它会一直产生正确的结果。 

要保证线程安全,重点是保护共享资源,如全局变量、静态局部变量。为了解释线程安全,可以模拟实现一个sleep函数。

#include 
#include 
#include 

void handler(int sig)//信号捕捉处理函数
{
	printf("i am handler, alarm ringing\n");
}
int mysleep(int timeout)
{

	struct sigaction act, oact;
	act.sa_handler = handler;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGALRM, &act, &oact);
	
	alarm(timeout);
	pause();
	
	int ret = alarm(0);//闹钟置空
	sigaction(SIGALRM, &oact, &act);
	return ret;//ret接收了上次闹钟剩余时间,若不等于0出错
}

这个函数有一个很隐蔽的错误,假设在设定闹钟之后,当前线程被切到后台等待,而这个线程的优先级非常低,当他恢复到前台时,

闹钟设定的信号已经发送,而线程没有收到,继续执行pause,此时永远不会被唤醒。

想要解决只要分四步:

1.屏蔽信号

2.设定闹钟

3.解除屏蔽

4.pause

但是,这个方法也有危险,在3、4步之间也有可能被切出去,只能把pause换成sigsuspend来解决。

理清了思路,把代码做如下修改

#include 
#include 
#include 

void handler(int sig)
{
	printf("i am handler, alarm ringing\n");
}

int mysleep(int timeout)
{
	struct sigaction act, oact;
	act.sa_handler = handler;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGALRM, &act, &oact);

	sigset_t mask, omask, suspmask;
	sigemptyset(&mask);
	sigaddset(&mask, SIGALRM);
	sigprocmask(SIG_BLOCK, &mask, &omask);

	alarm(timeout);
	
	suspmask = omask;
	sigdelset(&suspmask, SIGALRM);
	sigsuspend(&suspmask);

	int ret = alarm(0);
	sigaction(SIGALRM, &oact, &act);
	sigprocmask(SIG_SETMASK, &omask, NULL);
	return ret;
}


可重入函数

一个函数被称作可重入的,当且仅当可以被多个执行流进入而保证不出错

可重入与线程安全并不等同。一般说来,可重入的函数一定是线程安全的,但反过来不一定成立

一个单链表插入函数分三步:创建node、node指向head指向的节点 、head指向node

当多个执行流进入这个函数时,就会出问题

可重入函数和线程安全的对比_第1张图片



你可能感兴趣的:(Linux)