localtime线程不安全与localtime_r时区问题

localtime线程不安全,localtime_r时区问题

1. 代码实现

#include <time.h>

/* The C Standard says that localtime and gmtime return the same pointer.  */
struct tm _tmbuf;

/* Return the `struct tm' representation of *T in local time,
   using *TP to store the result.  */
struct tm *
__localtime_r (t, tp)
     const time_t *t;
     struct tm *tp;
{
  return __tz_convert (t, 1, tp);
}
weak_alias (__localtime_r, localtime_r)

/* Return the `struct tm' representation of *T in local time.  */
struct tm *
localtime (t)
     const time_t *t;
{
  return __tz_convert (t, 1, &_tmbuf);
}
libc_hidden_def (localtime)

都是调用__tz_convert(),区别在于:

  • localtime传参及返回值为库中全局变量_tmbuf
  • localtime_r参数及返回值都为上层的传参

2. 线程安全问题

2.1 原因

The four functions asctime(), ctime(), gmtime() and localtime() return a pointer to static data and hence are not thread-safe.

  • localtime调用__tz_convert()第三个参数为全局变量_tmbuf,并且其返回值也是_tmbuf的指针。返回指针所指向的全局变量的值可能被其他线程调用localtime给修改掉。所以localtime是线程不安全的。
  • localtime_r改用调用者传入的变量指针为参数,每个线程都会传入自己的变量指针,所以是线程安全的。
2.2 解决线程不安全
  • 网上有看到一种localtime_r的实现,直接调用localtime,说改成了线程安全的实现了,其实依旧是线程不安全的。也许需要再加一层锁,不然问题一样。
static struct tm * 
localtime_r (const time_t *t, struct tm *tp) 
{  
  struct tm *l = localtime (t);  
  if (! l)    
   return 0;   
  *tp = *l;  
  return tp; 
  }
  • 最好的方法可能是将localtime替换为调用glibc中的localtime_r,但localtime_r有时区问题

3. 时区问题

According to POSIX.1-2004, localtime() is required to behave as though tzset(3) was called, while localtime_r() does not have this requirement. For portable code tzset(3) should be called before localtime_r().
翻译:localtime像调用过了tzset一样。localtime_r没有,所以之前需要主动调用tzset,否则时区更改后localtime_r并不会生效。

3.1 怎么理解“localtime像调用过了tzset一样”

tzset的实现就是调用tzset_internal,而__tz_convert中也会调用。

  • localtime调用时(tp == &_tmbuf)成立为1,所以和tzset效果差不多,会去根据当前时区更新数据;
  • 而localtime_r则为0,只会在初始化的时候更新一次时区数据。因此在时区更改之后,需要在调用localtime_r之前主动调用tzset,否则获取的时间就不会随时区而变化。
struct tm * 
__tz_convert (const time_t *timer, int use_localtime, struct tm *tp) 
{    
   ......   
    
  __libc_lock_lock (tzset_lock);   //加锁   
  tzset_internal (tp == &_tmbuf, 0);
   ...... //时间转换逻辑   
  __libc_lock_unlock (tzset_lock); //释放锁     
   ...... 
  return tp; 
}
void
__tzset (void)
{
  __libc_lock_lock (tzset_lock);
  tzset_internal (1, 1);
  if (!__use_tzfile)
    {
      /* Set `tzname'.  */
      __tzname[0] = (char *) tz_rules[0].name;
      __tzname[1] = (char *) tz_rules[1].name;
    }
  __libc_lock_unlock (tzset_lock);
}
weak_alias (__tzset, tzset)
3.2 解决localtime_r时区问题
  • 封装新接口。需要将工程中的localtime_r全部替换。
struct tm *
my_localtime_r (t, tp)
     const time_t *t;
     struct tm *tp;
{
  tzset();
  return localtime_r(t, tp);
}
  • 重构localtime_r

4.死锁问题

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值