免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1242 | 回复: 0
打印 上一主题 下一主题

线程安全 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-04-23 17:17 |只看该作者 |倒序浏览

http://topic.csdn.net/u/20070530/10/ec332c1b-fdf6-4f3b-b90d-2d79b2f04a80.html

一个函数被称作“线程安全”的,当且仅当它被多个线程反复调用时,它会一直产生令人期待的正确的结果。反之为“线程不安全”函数,它主要有两种类型:
(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是线程安全函数,而不是可重入函数。




线程安全   和   可重入   其实是2个东西,
不过一般情况下可以认为
  可重入函数都是  
线程安全的
也有其他   情况,这个要看定义了,比如   
void   foo(int   *i)
{
  int   b   =   *i;
  b++;
  *i   =   b;
};
这个函数不是线程安全的,但是可重入
如果一个函数使用线程本地存储,互斥体等,也可以实现线程安全,但是可以不是   可重入的


这两个不是一个意思。但有时候有重叠。在多线程情况下,县城安全是可重入的必要条件。但是线程安全不一定能保证可重入。
比如单线程程序的函数,当然不存在线程安全问题,但不一定就是可重入的。比较经典的例子是最早DOS的TSR程序,由于用了同一堆栈指针,函数重入的时候会把栈写坏。还有比如Fortran一类的不支持递归的语言,由于变量是静态地址,无法可重入。
总之对于可重入函数,里面不能有大家同时可以用的公共资源。比如下面程序:
int   阶乘(int  
n)
{
      static   int   i   =   1;
      if   (n)
          i   =   i   *   阶乘(n-1);
      else
          i   =   0;
      return   i;
}
即使单线程也会出问题。


Chiyer(星羽)   兄给出了全局变量的线程安全处理示例:这种情况说明的是多个线程实例并发操作同一存储区域的数据的问题,就应用角度来说,本身就是不可重入的。
可重入的定义请翻阅相关课本。
微软早期的OS中的多线程是不可重入的,比如下面这个线程函数并发   3
  次
void   fun(void*p)
{
        int   i   =   0;
        ++i;
}
最后得到的结果可能是
  1,2,3   中任何结果。可能当时微软的多线程函数中的局部变量不是采用的堆栈,而是采用的共享存储。当时要做到线程函数中的局部变量相互独立(即保证线程安全,也可
以说保证线程函数可重入----但可重入表现在计算科学各方面,不只是线程),Borland
  提供了   C++
  多线程库。



说一个不可重入,但是线程安全的例子,帮助大家理解
funca()
{
lock(a);
......//interrupt by signal and call funca(), so dead lock
unlock(a);
}
int main()
{
signal(sig, funca);
funca();
}




本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/94916/showart_1908376.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP