一个函数被称作“线程安全”的,当且仅当它被多个线程反复调用时,它会一直产生令人期待的正确的结果。反之为“线程不安全”函数,它主要有两种类型:
(1)不保护共享变量的函数,包括全局变量和本地静态变量(static)。
我们可以使用POSIX信号量加以解决:
Sem_t
mutex;
Sem_init(&mutex,0,1);
Sem_wait(&mutex);
……
……
Sem_post(&mutex);
优点:简单易行;
缺点:同步操作p、v将影响程序的执行时间。
(2)返回指向静态变量的指针的函数,如gethostbyname,gethostbyaddr,inet_ntoa等等。
我一般这样使用gethodtbyname:
Struct hostent *hostp;
Hostp=gethostbyname(scorpion.cublog.cn);
……
……
Gethostbyname将执行结果存放在自己的static struct hostent变量中,并返回一个指向这个结构的指针。如果我们从并发线程中调用这个函数,那么将发生不可预料的结果,正在被一个线程使用的结果,可能悄悄地被其它线程覆盖。
解决办法:(a)、改写库函数gethostbyname,增加一个参数,将本地结构变量传入gethostbyname,
如:
Struct hostent host;
Gethostbyname(&host,scorpion.cublog.cn);
再使用host方可!
(b)、改写库函数gethostbyname,不是返回指针,而是直接返回struct hostent.
缺点:struct hostent结构较大,影响函数的效率。
以上两种办法(a)、(b),要求修改库函数,基本上不可行!
(c)、使用lock –and-copy技术,即定义自己的封装函数,通过调用它来取代直接使用gethostbyname.
struct hostent
*gethostbyname_my(char *hostname)
{
struct hostent *shar,*unshar;
unshar=malloc(sizeof(struct hostent))
sem_wait(&mutex);
shar=gethosybyname(hostname);
*unshar=*shar;
srm_post(&mutex);
return unshar;
}
现在大多数unix\linux操作系统,提供了线程不安全函数的可重入版本,如:gethostbyname_r。但是不同的系统可能接口不一样,所以建议不使用它们!还是使用我们自己的封装函数。
可重入函数,是线程安全函数的一种。特点:当它们被多个线程调用时,不会引用任何共享数据,也就是不引用静态或全局变量。如,gethostbyname_my是线程安全函数,而不是可重入函数。
参考书目:《深入理解计算机系统》、《UNIX网络编程 卷1》
|